Added auto scroll when text is too long

This commit is contained in:
nutty
2026-07-23 15:15:44 +10:00
parent 6795224fea
commit 1c3ddbd907
14 changed files with 389 additions and 148 deletions
+2 -2
View File
@@ -53,8 +53,8 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
// Wait for fade (0.5s), then swap the real text and hide overlay
setTimeout(() => {
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
SetLabelText('track-label', swapArtistTrack ? mediaProps.Artist : mediaProps.Title);
SetLabelText('artist-label', swapArtistTrack ? mediaProps.Title : mediaProps.Artist);
// Extract the image source string (use fallback if Windows has no art)
const newArtUrl = mediaProps.Thumbnail ?? './images/placeholder.png';
+9 -9
View File
@@ -7,13 +7,12 @@
width: 500px;
display: flex;
flex-direction: row;
align-items: stretch;
align-items: stretch;
gap: 0.5em;
opacity: 0;
}
#album-art-container {
/* 1. Tell it to maintain a 1:1 square ratio */
aspect-ratio: 1 / 1;
overflow: hidden;
border-radius: var(--component-radius);
@@ -85,11 +84,12 @@
#song-info-wrapper {
position: relative; /* Keeps it in the document flow */
z-index: 2; /* Sits above the background-layer */
padding: 10% 7.5%;
display: flex;
flex-direction: column;
gap: 0.3em;
max-height: 100%;
}
#track-label {
@@ -134,9 +134,9 @@
border-radius: 10000px;
background-color: var(--accent-color, #ffffff);
transition: width 1s ease-in-out, background-color 1s ease-in-out;
/* This makes the handle track the end of the bar */
position: relative;
position: relative;
}
#progress-bar-fill::after {
@@ -145,16 +145,16 @@
right: -6px; /* Adjust to center the circle on the end of the bar */
top: 50%;
transform: translateY(-50%);
width: 0.8em; /* Diameter of your circle */
height: 0.8em;
background-color: var(--accent-color, #ffffff);
border-radius: 50%;
filter: brightness(1.4);
filter: brightness(1.4);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); /* Adds depth */
/* Optional: Hide it if the width is too small */
opacity: 0;
opacity: 0;
transition: all 1s ease-in-out;
}
+2 -2
View File
@@ -53,8 +53,8 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
// Wait for fade (0.5s), then swap the real text and hide overlay
setTimeout(async () => {
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
SetLabelText('track-label', swapArtistTrack ? mediaProps.Artist : mediaProps.Title);
SetLabelText('artist-label', swapArtistTrack ? mediaProps.Title : mediaProps.Artist);
// Extract the image source string (use fallback if Windows has no art)
const newArtUrl = mediaProps.Thumbnail ?? './images/placeholder.png';
+2 -2
View File
@@ -64,7 +64,7 @@
flex-direction: row;
align-items: center;
overflow: hidden;
gap: 0.5em;
gap: 1em;
transition: all 0.25s ease-in-out;
}
@@ -99,7 +99,7 @@
position: relative; /* Keeps it in the document flow */
z-index: 2; /* Sits above the background-layer */
padding: 1em 1.75em;
padding: 1em 1.3em;
display: flex;
flex-direction: column;
gap: 0.3em;
+67 -31
View File
@@ -48,23 +48,28 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
// Wait for fade (0.5s), then swap the real text and hide overlay
setTimeout(() => {
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
trackLabelBackground.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
artistLabelBackground.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
// trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
// artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
// trackLabelBackground.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
// artistLabelBackground.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
if (swapArtistTrack) {
SetSongInfo(mediaProps.Artist, mediaProps.Title);
} else {
SetSongInfo(mediaProps.Title, mediaProps.Artist);
}
// Extract the image source string (use fallback if Windows has no art)
// Set the pill color
switch (themeVariant) {
case "compact-inverted":
songInfoContainer.style.backgroundColor = accentColorPalette.LightVibrant;
progressBarTrack.style.backgroundColor = accentColorPalette.LightVibrant;
progressBar.style.background = `color-mix(in srgb, ${accentColorPalette.DarkMuted}, black 60%)`;
songLabel.style.color = accentColorPalette.LightVibrant;
songLabelBackground.style.color = accentColorPalette.DarkVibrant;
break;
default:
songInfoContainer.style.backgroundColor = `color-mix(in srgb, ${accentColorPalette.DarkMuted}, black 60%)`;
progressBarTrack.style.backgroundColor = `color-mix(in srgb, ${accentColorPalette.DarkMuted}, black 60%)`;
progressBar.style.background = accentColorPalette.LightVibrant;
songLabel.style.color = accentColorPalette.DarkVibrant;
songLabelBackground.style.color = accentColorPalette.LightVibrant;
@@ -74,30 +79,6 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
songLabel.style.opacity = "";
songLabelBackground.style.opacity = "";
// // If text is too long, add a marquee
// requestAnimationFrame(() => {
// const containerWidth = songInfoContainer.clientWidth;
// const textWidth = trackLabel.clientWidth + artistLabel.clientWidth;
// console.log(containerWidth);
// console.log(textWidth);
// if (textWidth > containerWidth) {
// songLabel.classList.add('scrolling-text');
// songLabelBackground.classList.add('scrolling-text');
// // Calculate speed: 50 pixels per second is a good standard
// const duration = (textWidth + containerWidth) / 50;
// console.log(duration);
// document.documentElement.style.setProperty('--scroll-duration', `${duration}s`);
// document.documentElement.style.setProperty('--container-width', `${textWidth}px`);
// document.documentElement.style.setProperty('--text-width', `-${textWidth}px`);
// } else {
// songLabel.classList.remove('scrolling-text');
// songLabelBackground.classList.remove('scrolling-text');
// }
// });
setTimeout(() => {
SetVisibility(true); // Show the overlay for a few seconds if autoHide is enabled
}, 250);
@@ -127,7 +108,62 @@ function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) {
// Update the clip-path
progressBar.style.clipPath = `inset(0 ${clipRight}% 0 0)`;
progressBarTrack.style.clipPath = `inset(0 0 0 ${progressPercent}%)`;
// // Set the progress bar but with a transition for smoothness
// const clipRight = progressPercent;
// progressBar.style.maskImage = `linear-gradient(to right, black calc(${clipRight}% - 4em), transparent ${clipRight}%)`
// const transitionWidth = '1em';
// progressBar.style.maskImage = `linear-gradient(to right, black calc(${clipRight}% - ${transitionWidth}), transparent ${clipRight}%)`
// progressBarTrack.style.maskImage = `linear-gradient(to right, transparent calc(${clipRight}% - ${transitionWidth}), black ${clipRight}%)`;
}
// MARQUEE LOGIC
// This is a more advanced marquee implementation that calculates the overflow distance and adjusts the scroll speed accordingly
// Call this function whenever the track changes
function SetSongInfo(trackName, artistName) {
// Update main text and background layer simultaneously
UpdateSingleSongLabel('song-label', trackName, artistName);
UpdateSingleSongLabel('song-label-background', trackName, artistName);
}
function UpdateSingleSongLabel(containerId, trackName, artistName) {
const container = document.getElementById(containerId);
if (!container) return;
// 1. Structure the inner HTML with a wrapper so we can measure exact inline width
container.classList.remove('is-overflowing');
container.innerHTML = `
<span class="scroll-content">
<span class="track-label">${trackName}</span>
<span class="artist-label">${artistName}</span>
</span>
`;
const scrollContent = container.querySelector('.scroll-content');
// 2. Measure total width of (Track + Artist) against parent width
const textWidth = scrollContent.scrollWidth;
const containerWidth = container.clientWidth;
const overflowDistance = textWidth - containerWidth;
// 3. If the combined row overflows, set the CSS variables and add class
if (overflowDistance > 0) {
const distancePercent = (overflowDistance / textWidth) * 100;
const travelTime = overflowDistance / SCROLL_SPEED_PX_PER_SEC;
const totalDuration = (travelTime * 2) + 3; // 3 seconds total for start/end pauses
scrollContent.style.setProperty('--scroll-distance', `-${distancePercent}%`);
container.style.setProperty('--marquee-duration', `${totalDuration}s`);
container.classList.add('is-overflowing');
}
// 4. Attach ResizeObserver once to handle responsive window resizes
if (!container._resizeObserver) {
container._resizeObserver = new ResizeObserver(() => {
UpdateSingleSongLabel(containerId, trackName, artistName);
});
container._resizeObserver.observe(container);
}
}
+36 -16
View File
@@ -35,6 +35,10 @@
-webkit-mask-image: linear-gradient(to right, black calc(100% - 4em), transparent 100%); */
}
#progress-bar-track {
transition: all 1s ease-in-out;
}
.progress {
box-sizing: border-box;
padding: 0.25em 0.75em;
@@ -53,7 +57,7 @@
gap: 0.5em;
min-width: 100%;
justify-content: var(--justify-content);
overflow: visible;
overflow: hidden;
}
#artist-label,
@@ -69,23 +73,39 @@
}
.text-label {
white-space: nowrap;
/* overflow: hidden; */
transition: opacity 0.25s ease-in-out;
}
/* TODO: Marquee doesn't work that well, disabling for now */
/* .scrolling-text {
animation: marquee var(--scroll-duration, 15s) linear infinite;
display: inline-block;
/* Inner wrapper holding track + artist inline */
#song-label .scroll-content,
#song-label-background .scroll-content {
display: inline-flex;
flex-direction: row;
align-items: baseline;
gap: 0.5em; /* Spacing between Track and Artist */
white-space: nowrap;
will-change: transform;
}
@keyframes marquee {
0% {
transform: translateX(var(--container-width));
}
100% {
transform: translateX(var(--text-width));
}
} */
/* Preserve original font weights */
.track-label {
font-weight: 700;
}
.artist-label {
font-size: 0.8em;
font-weight: 400;
opacity: 0.7;
}
/* Marquee scroll animation */
#song-label.is-overflowing .scroll-content,
#song-label-background.is-overflowing .scroll-content {
animation: ping-pong-scroll var(--marquee-duration, 8s) linear infinite;
}
/* Mask animation applied to container */
#song-label.is-overflowing,
#song-label-background.is-overflowing {
animation: ping-pong-mask var(--marquee-duration, 8s) linear infinite;
}
+13 -14
View File
@@ -1,20 +1,19 @@
<div id="main-wrapper">
<div id="yet-another-wrapper-yup">
<div id="album-art-container">
<div id="album-art-layer"></div>
<div id="album-art-transition-layer"></div>
</div>
<div id="album-art-container">
<div id="album-art-layer"></div>
<div id="album-art-transition-layer"></div>
</div>
<div id="song-info-container">
<div id="track-label" class="text-label">&nbsp;</div>
<div id="artist-label" class="text-label">&nbsp;</div>
<div id="progress-container">
<label>
<span id="current-time" class="progress-text">0:00</span>
<span>/</span>
<span id="duration" class="progress-text">0:00</span>
</label>
<div id="song-info-container">
<div id="track-label" class="text-label">&nbsp;</div>
<div id="artist-label" class="text-label">&nbsp;</div>
<div id="progress-container">
<div id="current-time" class="progress-text">0:00</div>
<div id="progress-bar-track">
<div id="progress-bar-fill"></div>
<div></div>
</div>
<div id="duration" class="progress-text">0:00</div>
</div>
</div>
</div>
+47 -5
View File
@@ -10,6 +10,7 @@ const albumArtTransition = document.getElementById('album-art-transition-layer')
const trackLabel = document.getElementById('track-label');
const artistLabel = document.getElementById('artist-label');
const progressContainer = document.getElementById('progress-container');
const progressBarFill = document.getElementById('progress-bar-fill');
const currentTimeLabel = document.getElementById('current-time');
const durationLabel = document.getElementById('duration');
@@ -34,8 +35,9 @@ if (!showProgressBar)
progressContainer.style.display = 'none';
// If text alignment is 'right', swap the album art to the right hand side too
if (textAlignment == 'right')
document.getElementById('yet-another-wrapper-yup').appendChild(document.getElementById('album-art-container'));
if (textAlignment == 'right') {
mainWrapper.style.flexDirection = 'row-reverse';
}
@@ -52,8 +54,10 @@ async function ChangeTrack(mediaProps) {
// Wait for fade (0.5s), then swap the real text and hide overlay
setTimeout(() => {
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
// trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
// artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
SetLabelText('track-label', swapArtistTrack ? mediaProps.Artist : mediaProps.Title);
SetLabelText('artist-label', swapArtistTrack ? mediaProps.Title : mediaProps.Artist);
// Extract the image source string (use fallback if Windows has no art)
const newArtUrl = mediaProps.Thumbnail ?? './images/placeholder.png';
@@ -81,4 +85,42 @@ function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) {
durationLabel.innerText =
ConvertMillisecondsToHoursMinutesSecondsSoItLooksBetterAndNotCringe(timelineProps.EndTime);
}
// Set progressbar
// Ensure we don't divide by zero or exceed 100%
const durationMs = timelineProps.EndTime;
let progressPercent = durationMs > 0 ? (currentPositionMs / durationMs) * 100 : 0;
progressPercent = Math.min(100, Math.max(0, progressPercent));
progressBarFill.style.width = `${progressPercent}%`;
progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant);
switch (themeVariant) {
case "matte":
progressBarFill.style.setProperty('--accent-color', accentColorPalette.DarkVibrant);
break;
case "matte-dark":
default:
progressBarFill.style.setProperty('--accent-color', accentColorPalette.LightVibrant);
break;
}
}
// Helper function to calculate height of album art
const observer = new ResizeObserver(entries => {
for (let entry of entries) {
// Get the accurate rendered height of song-info-container
const infoHeight = entry.contentRect.height;
// Calculate 125% of that height
const targetSize = infoHeight * 1.5;
console.log(targetSize);
// Apply to album art (setting both width & height ensures it stays square)
albumArtContainer.style.height = `${targetSize}px`;
albumArtContainer.style.width = `${targetSize}px`;
}
});
// Start watching the song info container for size changes
observer.observe(songInfoContainer);
+33 -28
View File
@@ -6,23 +6,13 @@
width: 500px;
display: flex;
flex-direction: row;
align-items: stretch;
opacity: 0;
}
#yet-another-wrapper-yup {
position: relative;
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
gap: 0.75em;
transition: all 0.25s ease-in-out;
gap: 1em;
opacity: 0;
}
#album-art-container {
/* 1. Tell it to maintain a 1:1 square ratio */
height: 150%;
aspect-ratio: 1 / 1;
overflow: hidden;
border-radius: var(--border-radius);
@@ -62,23 +52,13 @@
overflow: hidden;
flex-grow: 1;
min-width: 0;
gap: 0.25em;
}
#background-transition-layer {
z-index: 0;
}
/* The content sits on top */
#song-info-wrapper {
position: relative; /* Keeps it in the document flow */
z-index: 2; /* Sits above the background-layer */
padding: 1em 1.75em;
display: flex;
flex-direction: column;
gap: 0.3em;
}
#track-label {
font-size: 1em;
font-weight: 600;
@@ -93,11 +73,36 @@
}
#progress-container {
font-size: 0.6em;
font-weight: 400;
opacity: 0.6;
text-align: var(--text-alignment);
margin-top: 0.5em;
display: flex;
flex-direction: row;
align-items: center;
gap: 0.2em;
margin-top: 0.2em;
gap: 0.5em;
}
#progress-bar-track {
width: 100%;
height: 0.4em;
border-radius: 1em;
background-color: #ffffff3a;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); /* Adds depth */
}
#progress-bar-fill {
height: 100%;
width: 0%; /* JS will control this */
border-radius: 1em;
background-color: var(--accent-color, #ffffff);
transition: width 1s ease-in-out, background-color 1s ease-in-out;
/* This makes the handle track the end of the bar */
position: relative;
}
.progress-text {
font-size: 0.7em;
opacity: 0.5;
}
.text-label {
-3
View File
@@ -20,9 +20,6 @@
<div></div>
</div>
<div id="duration" class="progress-text">0:00</div>
<div id="time-container">
</div>
</div>
</div>
</div>
+9 -2
View File
@@ -5,6 +5,7 @@
const mainWrapper = document.getElementById('main-wrapper');
const albumArtContainer = document.getElementById('album-art-container');
const songInfoContainer = document.getElementById('song-info-container');
const songInfoWrapper = document.getElementById('song-info-wrapper');
const albumArtLayer = document.getElementById('album-art-layer');
const albumArtTransition = document.getElementById('album-art-transition-layer');
const backgroundLayer = document.getElementById('background-layer');
@@ -48,6 +49,10 @@ switch (themeVariant) {
break;
}
// If text alignment is 'right', swap the album art to the right hand side too
if (textAlignment == 'right') {
songInfoContainer.style.flexDirection = 'row-reverse';
}
////////////////////
@@ -65,8 +70,8 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
// Wait for fade (0.5s), then swap the real text and hide overlay
setTimeout(async () => {
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
SetLabelText('track-label', swapArtistTrack ? mediaProps.Artist : mediaProps.Title);
SetLabelText('artist-label', swapArtistTrack ? mediaProps.Title : mediaProps.Artist);
switch (themeVariant) {
case "matte":
@@ -100,6 +105,8 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
backgroundLayer.style.opacity = "";
albumArtLayer.style.opacity = "";
console.log(songInfoWrapper.clientWidth);
setTimeout(() => {
// Set the image
backgroundTransitionLayer.style.backgroundImage = `url('${newArtUrl}')`;
+1 -17
View File
@@ -7,7 +7,7 @@
width: 500px;
display: flex;
flex-direction: row;
align-items: stretch;
align-items: stretch;
gap: 0.5em;
opacity: 0;
}
@@ -115,14 +115,6 @@
text-align: var(--text-alignment);
}
.text-label {
white-space: nowrap;
overflow: hidden;
mask-image: var(--trailing-fade);
-webkit-mask-image: var(--trailing-fade);
transition: opacity 0.25s ease-in-out;
}
#progress-container {
display: flex;
flex-direction: row;
@@ -151,14 +143,6 @@
position: relative;
}
/* #time-container {
display: flex;
justify-content: space-between;
font-size: 0.7em;
opacity: 0.5;
display: none;
} */
.progress-text {
font-size: 0.7em;
opacity: 0.5;