mirror of
https://github.com/nuttylmao/nutty.gg.git
synced 2026-09-19 04:00:59 -04:00
Added auto scroll when text is too long
This commit is contained in:
+57
-15
@@ -11,6 +11,7 @@ const urlParams = new URLSearchParams(queryString);
|
|||||||
|
|
||||||
const REQUIRED_VERSION = '0.0.2';
|
const REQUIRED_VERSION = '0.0.2';
|
||||||
const SMTC_BRIDGE_DOWNLOAD_URL = 'https://github.com/nuttylmao/smtc-bridge/releases';
|
const SMTC_BRIDGE_DOWNLOAD_URL = 'https://github.com/nuttylmao/smtc-bridge/releases';
|
||||||
|
let VersionChecked = false;
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
// PAGE ELEMENTS //
|
// PAGE ELEMENTS //
|
||||||
@@ -70,21 +71,6 @@ else
|
|||||||
|
|
||||||
// Set text alignment
|
// Set text alignment
|
||||||
document.documentElement.style.setProperty('--text-alignment', `${textAlignment}`);
|
document.documentElement.style.setProperty('--text-alignment', `${textAlignment}`);
|
||||||
switch (textAlignment)
|
|
||||||
{
|
|
||||||
case 'left':
|
|
||||||
document.documentElement.style.setProperty('--justify-content', `flex-start`);
|
|
||||||
document.documentElement.style.setProperty('--trailing-fade', `linear-gradient(to right, black calc(100% - 1em), transparent 100%)`);
|
|
||||||
break;
|
|
||||||
case 'center':
|
|
||||||
document.documentElement.style.setProperty('--justify-content', `center`);
|
|
||||||
document.documentElement.style.setProperty('--trailing-fade', `linear-gradient(to right, black calc(100% - 1em), transparent 100%)`);
|
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
document.documentElement.style.setProperty('--justify-content', `flex-end`);
|
|
||||||
document.documentElement.style.setProperty('--trailing-fade', ``);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -167,7 +153,10 @@ async function FetchMedia() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check the SMTC Bridge version
|
// Check the SMTC Bridge version
|
||||||
|
if (!VersionChecked) {
|
||||||
CheckSMTCBridgeVersion(data.app_version);
|
CheckSMTCBridgeVersion(data.app_version);
|
||||||
|
VersionChecked = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Update the UI with the received data
|
// Update the UI with the received data
|
||||||
// console.log(data);
|
// console.log(data);
|
||||||
@@ -411,3 +400,56 @@ function SetVisibility(visible) {
|
|||||||
mainWrapper.style.animation = `${hideAnimation} 0.5s ease-out forwards`;
|
mainWrapper.style.animation = `${hideAnimation} 0.5s ease-out forwards`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARQUEE LOGIC
|
||||||
|
// This is a more advanced marquee implementation that calculates the overflow distance and adjusts the scroll speed accordingly
|
||||||
|
const labelData = new Map();
|
||||||
|
|
||||||
|
// Adjust this value to change scroll speed (pixels per second)
|
||||||
|
const SCROLL_SPEED_PX_PER_SEC = 40;
|
||||||
|
|
||||||
|
function SetLabelText(elementId, text) {
|
||||||
|
const label = document.getElementById(elementId);
|
||||||
|
if (!label) return;
|
||||||
|
|
||||||
|
labelData.set(elementId, text);
|
||||||
|
ApplyMarquee(label, text);
|
||||||
|
|
||||||
|
// Keep it responsive on container resize
|
||||||
|
if (!label._resizeObserver) {
|
||||||
|
label._resizeObserver = new ResizeObserver(() => {
|
||||||
|
const currentText = labelData.get(elementId);
|
||||||
|
if (currentText) ApplyMarquee(label, currentText);
|
||||||
|
});
|
||||||
|
label._resizeObserver.observe(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ApplyMarquee(label, text) {
|
||||||
|
// 1. Render single span to measure dimensions
|
||||||
|
label.classList.remove('is-overflowing');
|
||||||
|
label.innerHTML = `<span class="scroll-content">${text}</span>`;
|
||||||
|
|
||||||
|
const scrollContent = label.querySelector('.scroll-content');
|
||||||
|
|
||||||
|
const textWidth = scrollContent.scrollWidth;
|
||||||
|
const containerWidth = label.clientWidth;
|
||||||
|
const overflowDistance = textWidth - containerWidth;
|
||||||
|
|
||||||
|
// 2. Check if text exceeds container
|
||||||
|
if (overflowDistance > 0) {
|
||||||
|
// Calculate travel distance in percentage relative to scrollContent's width
|
||||||
|
const distancePercent = (overflowDistance / textWidth) * 100;
|
||||||
|
|
||||||
|
// Compute duration to keep speed consistent regardless of length
|
||||||
|
// We multiply travel time by 2 (for both directions) plus 3 seconds for pauses
|
||||||
|
const travelTime = overflowDistance / SCROLL_SPEED_PX_PER_SEC;
|
||||||
|
const totalDuration = (travelTime * 2) + 3;
|
||||||
|
|
||||||
|
// Pass calculated variables to CSS
|
||||||
|
scrollContent.style.setProperty('--scroll-distance', `-${distancePercent}%`);
|
||||||
|
label.style.setProperty('--marquee-duration', `${totalDuration}s`);
|
||||||
|
|
||||||
|
label.classList.add('is-overflowing');
|
||||||
|
}
|
||||||
|
}
|
||||||
+110
-1
@@ -10,7 +10,8 @@ body {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
margin: 0.5em;
|
padding: 0.5em;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -141,3 +142,111 @@ body {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@property --mask-left {
|
||||||
|
syntax: '<color>';
|
||||||
|
inherits: false;
|
||||||
|
initial-value: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property --mask-right {
|
||||||
|
syntax: '<color>';
|
||||||
|
inherits: false;
|
||||||
|
initial-value: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-label {
|
||||||
|
--fade-width: 0.5em;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
mask-image: var(--trailing-fade);
|
||||||
|
-webkit-mask-image: var(--trailing-fade);
|
||||||
|
transition: opacity 0.25s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-label:empty {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-label:has(.scroll-content:empty),
|
||||||
|
.text-label:has(#track-label:empty) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-label .scroll-content {
|
||||||
|
display: inline-flex;
|
||||||
|
white-space: nowrap;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-label.is-overflowing .scroll-content {
|
||||||
|
animation: ping-pong-scroll var(--marquee-duration, 8s) linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-label.is-overflowing {
|
||||||
|
animation: ping-pong-mask var(--marquee-duration, 8s) linear infinite;
|
||||||
|
|
||||||
|
/* Static gradient structure referencing dynamic color variables */
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
var(--mask-left) 0px,
|
||||||
|
black var(--fade-width),
|
||||||
|
black calc(100% - var(--fade-width)),
|
||||||
|
var(--mask-right) 100%
|
||||||
|
);
|
||||||
|
-webkit-mask-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
var(--mask-left) 0px,
|
||||||
|
black var(--fade-width),
|
||||||
|
black calc(100% - var(--fade-width)),
|
||||||
|
var(--mask-right) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ping-pong-scroll {
|
||||||
|
0%, 15% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
50%, 65% {
|
||||||
|
transform: translateX(var(--scroll-distance, -100%));
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ping-pong-mask {
|
||||||
|
/* 1. AT START: Left is solid (black), Right is faded (transparent) */
|
||||||
|
0%, 15% {
|
||||||
|
--mask-left: black;
|
||||||
|
--mask-right: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. MOVING LEFT: Left smoothly fades in (transparent), Right stays faded */
|
||||||
|
22%, 43% {
|
||||||
|
--mask-left: transparent;
|
||||||
|
--mask-right: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. AT FAR END: Right smoothly solidifies (black), Left stays faded */
|
||||||
|
50%, 65% {
|
||||||
|
--mask-left: transparent;
|
||||||
|
--mask-right: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. MOVING RIGHT: Right smoothly fades back out (transparent) */
|
||||||
|
72%, 93% {
|
||||||
|
--mask-left: transparent;
|
||||||
|
--mask-right: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 5. RETURN TO START: Left smoothly solidifies (black) */
|
||||||
|
100% {
|
||||||
|
--mask-left: black;
|
||||||
|
--mask-right: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,8 +53,8 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
|
|||||||
|
|
||||||
// Wait for fade (0.5s), then swap the real text and hide overlay
|
// Wait for fade (0.5s), then swap the real text and hide overlay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
|
SetLabelText('track-label', swapArtistTrack ? mediaProps.Artist : mediaProps.Title);
|
||||||
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
|
SetLabelText('artist-label', swapArtistTrack ? mediaProps.Title : mediaProps.Artist);
|
||||||
|
|
||||||
// Extract the image source string (use fallback if Windows has no art)
|
// Extract the image source string (use fallback if Windows has no art)
|
||||||
const newArtUrl = mediaProps.Thumbnail ?? './images/placeholder.png';
|
const newArtUrl = mediaProps.Thumbnail ?? './images/placeholder.png';
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#album-art-container {
|
#album-art-container {
|
||||||
/* 1. Tell it to maintain a 1:1 square ratio */
|
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: var(--component-radius);
|
border-radius: var(--component-radius);
|
||||||
@@ -90,6 +89,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.3em;
|
gap: 0.3em;
|
||||||
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#track-label {
|
#track-label {
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
|
|||||||
|
|
||||||
// Wait for fade (0.5s), then swap the real text and hide overlay
|
// Wait for fade (0.5s), then swap the real text and hide overlay
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
|
SetLabelText('track-label', swapArtistTrack ? mediaProps.Artist : mediaProps.Title);
|
||||||
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
|
SetLabelText('artist-label', swapArtistTrack ? mediaProps.Title : mediaProps.Artist);
|
||||||
|
|
||||||
// Extract the image source string (use fallback if Windows has no art)
|
// Extract the image source string (use fallback if Windows has no art)
|
||||||
const newArtUrl = mediaProps.Thumbnail ?? './images/placeholder.png';
|
const newArtUrl = mediaProps.Thumbnail ?? './images/placeholder.png';
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
gap: 0.5em;
|
gap: 1em;
|
||||||
transition: all 0.25s ease-in-out;
|
transition: all 0.25s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
position: relative; /* Keeps it in the document flow */
|
position: relative; /* Keeps it in the document flow */
|
||||||
z-index: 2; /* Sits above the background-layer */
|
z-index: 2; /* Sits above the background-layer */
|
||||||
|
|
||||||
padding: 1em 1.75em;
|
padding: 1em 1.3em;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.3em;
|
gap: 0.3em;
|
||||||
|
|||||||
@@ -48,23 +48,28 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
|
|||||||
|
|
||||||
// Wait for fade (0.5s), then swap the real text and hide overlay
|
// Wait for fade (0.5s), then swap the real text and hide overlay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
|
// trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
|
||||||
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
|
// artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
|
||||||
trackLabelBackground.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
|
// trackLabelBackground.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
|
||||||
artistLabelBackground.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
|
// 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)
|
// Extract the image source string (use fallback if Windows has no art)
|
||||||
|
|
||||||
// Set the pill color
|
// Set the pill color
|
||||||
switch (themeVariant) {
|
switch (themeVariant) {
|
||||||
case "compact-inverted":
|
case "compact-inverted":
|
||||||
songInfoContainer.style.backgroundColor = accentColorPalette.LightVibrant;
|
progressBarTrack.style.backgroundColor = accentColorPalette.LightVibrant;
|
||||||
progressBar.style.background = `color-mix(in srgb, ${accentColorPalette.DarkMuted}, black 60%)`;
|
progressBar.style.background = `color-mix(in srgb, ${accentColorPalette.DarkMuted}, black 60%)`;
|
||||||
songLabel.style.color = accentColorPalette.LightVibrant;
|
songLabel.style.color = accentColorPalette.LightVibrant;
|
||||||
songLabelBackground.style.color = accentColorPalette.DarkVibrant;
|
songLabelBackground.style.color = accentColorPalette.DarkVibrant;
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
progressBar.style.background = accentColorPalette.LightVibrant;
|
||||||
songLabel.style.color = accentColorPalette.DarkVibrant;
|
songLabel.style.color = accentColorPalette.DarkVibrant;
|
||||||
songLabelBackground.style.color = accentColorPalette.LightVibrant;
|
songLabelBackground.style.color = accentColorPalette.LightVibrant;
|
||||||
@@ -74,30 +79,6 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
|
|||||||
songLabel.style.opacity = "";
|
songLabel.style.opacity = "";
|
||||||
songLabelBackground.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(() => {
|
setTimeout(() => {
|
||||||
SetVisibility(true); // Show the overlay for a few seconds if autoHide is enabled
|
SetVisibility(true); // Show the overlay for a few seconds if autoHide is enabled
|
||||||
}, 250);
|
}, 250);
|
||||||
@@ -127,7 +108,62 @@ function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) {
|
|||||||
|
|
||||||
// Update the clip-path
|
// Update the clip-path
|
||||||
progressBar.style.clipPath = `inset(0 ${clipRight}% 0 0)`;
|
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;
|
// 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -35,6 +35,10 @@
|
|||||||
-webkit-mask-image: linear-gradient(to right, black calc(100% - 4em), transparent 100%); */
|
-webkit-mask-image: linear-gradient(to right, black calc(100% - 4em), transparent 100%); */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#progress-bar-track {
|
||||||
|
transition: all 1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
.progress {
|
.progress {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0.25em 0.75em;
|
padding: 0.25em 0.75em;
|
||||||
@@ -53,7 +57,7 @@
|
|||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
justify-content: var(--justify-content);
|
justify-content: var(--justify-content);
|
||||||
overflow: visible;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#artist-label,
|
#artist-label,
|
||||||
@@ -69,23 +73,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text-label {
|
.text-label {
|
||||||
white-space: nowrap;
|
|
||||||
/* overflow: hidden; */
|
|
||||||
transition: opacity 0.25s ease-in-out;
|
transition: opacity 0.25s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inner wrapper holding track + artist inline */
|
||||||
/* TODO: Marquee doesn't work that well, disabling for now */
|
#song-label .scroll-content,
|
||||||
/* .scrolling-text {
|
#song-label-background .scroll-content {
|
||||||
animation: marquee var(--scroll-duration, 15s) linear infinite;
|
display: inline-flex;
|
||||||
display: inline-block;
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.5em; /* Spacing between Track and Artist */
|
||||||
|
white-space: nowrap;
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes marquee {
|
/* Preserve original font weights */
|
||||||
0% {
|
.track-label {
|
||||||
transform: translateX(var(--container-width));
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
100% {
|
|
||||||
transform: translateX(var(--text-width));
|
.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;
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
<div id="main-wrapper">
|
<div id="main-wrapper">
|
||||||
<div id="yet-another-wrapper-yup">
|
|
||||||
<div id="album-art-container">
|
<div id="album-art-container">
|
||||||
<div id="album-art-layer"></div>
|
<div id="album-art-layer"></div>
|
||||||
<div id="album-art-transition-layer"></div>
|
<div id="album-art-transition-layer"></div>
|
||||||
@@ -9,12 +8,12 @@
|
|||||||
<div id="track-label" class="text-label"> </div>
|
<div id="track-label" class="text-label"> </div>
|
||||||
<div id="artist-label" class="text-label"> </div>
|
<div id="artist-label" class="text-label"> </div>
|
||||||
<div id="progress-container">
|
<div id="progress-container">
|
||||||
<label>
|
<div id="current-time" class="progress-text">0:00</div>
|
||||||
<span id="current-time" class="progress-text">0:00</span>
|
<div id="progress-bar-track">
|
||||||
<span>/</span>
|
<div id="progress-bar-fill"></div>
|
||||||
<span id="duration" class="progress-text">0:00</span>
|
<div></div>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="duration" class="progress-text">0:00</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -10,6 +10,7 @@ const albumArtTransition = document.getElementById('album-art-transition-layer')
|
|||||||
const trackLabel = document.getElementById('track-label');
|
const trackLabel = document.getElementById('track-label');
|
||||||
const artistLabel = document.getElementById('artist-label');
|
const artistLabel = document.getElementById('artist-label');
|
||||||
const progressContainer = document.getElementById('progress-container');
|
const progressContainer = document.getElementById('progress-container');
|
||||||
|
const progressBarFill = document.getElementById('progress-bar-fill');
|
||||||
const currentTimeLabel = document.getElementById('current-time');
|
const currentTimeLabel = document.getElementById('current-time');
|
||||||
const durationLabel = document.getElementById('duration');
|
const durationLabel = document.getElementById('duration');
|
||||||
|
|
||||||
@@ -34,8 +35,9 @@ if (!showProgressBar)
|
|||||||
progressContainer.style.display = 'none';
|
progressContainer.style.display = 'none';
|
||||||
|
|
||||||
// If text alignment is 'right', swap the album art to the right hand side too
|
// If text alignment is 'right', swap the album art to the right hand side too
|
||||||
if (textAlignment == 'right')
|
if (textAlignment == 'right') {
|
||||||
document.getElementById('yet-another-wrapper-yup').appendChild(document.getElementById('album-art-container'));
|
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
|
// Wait for fade (0.5s), then swap the real text and hide overlay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
|
// trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
|
||||||
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
|
// 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)
|
// Extract the image source string (use fallback if Windows has no art)
|
||||||
const newArtUrl = mediaProps.Thumbnail ?? './images/placeholder.png';
|
const newArtUrl = mediaProps.Thumbnail ?? './images/placeholder.png';
|
||||||
@@ -81,4 +85,42 @@ function SetProgressInfo(timelineProps, currentPositionMs, accentColorPalette) {
|
|||||||
|
|
||||||
durationLabel.innerText =
|
durationLabel.innerText =
|
||||||
ConvertMillisecondsToHoursMinutesSecondsSoItLooksBetterAndNotCringe(timelineProps.EndTime);
|
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);
|
||||||
@@ -6,23 +6,13 @@
|
|||||||
width: 500px;
|
width: 500px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: stretch;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#yet-another-wrapper-yup {
|
|
||||||
position: relative;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.75em;
|
gap: 1em;
|
||||||
transition: all 0.25s ease-in-out;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#album-art-container {
|
#album-art-container {
|
||||||
/* 1. Tell it to maintain a 1:1 square ratio */
|
/* 1. Tell it to maintain a 1:1 square ratio */
|
||||||
height: 150%;
|
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
@@ -62,23 +52,13 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#background-transition-layer {
|
#background-transition-layer {
|
||||||
z-index: 0;
|
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 {
|
#track-label {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -93,11 +73,36 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#progress-container {
|
#progress-container {
|
||||||
font-size: 0.6em;
|
display: flex;
|
||||||
font-weight: 400;
|
flex-direction: row;
|
||||||
opacity: 0.6;
|
align-items: center;
|
||||||
text-align: var(--text-alignment);
|
gap: 0.2em;
|
||||||
margin-top: 0.5em;
|
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 {
|
.text-label {
|
||||||
|
|||||||
@@ -20,9 +20,6 @@
|
|||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="duration" class="progress-text">0:00</div>
|
<div id="duration" class="progress-text">0:00</div>
|
||||||
|
|
||||||
<div id="time-container">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
const mainWrapper = document.getElementById('main-wrapper');
|
const mainWrapper = document.getElementById('main-wrapper');
|
||||||
const albumArtContainer = document.getElementById('album-art-container');
|
const albumArtContainer = document.getElementById('album-art-container');
|
||||||
const songInfoContainer = document.getElementById('song-info-container');
|
const songInfoContainer = document.getElementById('song-info-container');
|
||||||
|
const songInfoWrapper = document.getElementById('song-info-wrapper');
|
||||||
const albumArtLayer = document.getElementById('album-art-layer');
|
const albumArtLayer = document.getElementById('album-art-layer');
|
||||||
const albumArtTransition = document.getElementById('album-art-transition-layer');
|
const albumArtTransition = document.getElementById('album-art-transition-layer');
|
||||||
const backgroundLayer = document.getElementById('background-layer');
|
const backgroundLayer = document.getElementById('background-layer');
|
||||||
@@ -48,6 +49,10 @@ switch (themeVariant) {
|
|||||||
break;
|
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
|
// Wait for fade (0.5s), then swap the real text and hide overlay
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
trackLabel.innerText = swapArtistTrack ? mediaProps.Artist : mediaProps.Title;
|
SetLabelText('track-label', swapArtistTrack ? mediaProps.Artist : mediaProps.Title);
|
||||||
artistLabel.innerText = swapArtistTrack ? mediaProps.Title : mediaProps.Artist;
|
SetLabelText('artist-label', swapArtistTrack ? mediaProps.Title : mediaProps.Artist);
|
||||||
|
|
||||||
switch (themeVariant) {
|
switch (themeVariant) {
|
||||||
case "matte":
|
case "matte":
|
||||||
@@ -100,6 +105,8 @@ async function ChangeTrack(mediaProps, accentColorPalette) {
|
|||||||
backgroundLayer.style.opacity = "";
|
backgroundLayer.style.opacity = "";
|
||||||
albumArtLayer.style.opacity = "";
|
albumArtLayer.style.opacity = "";
|
||||||
|
|
||||||
|
console.log(songInfoWrapper.clientWidth);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Set the image
|
// Set the image
|
||||||
backgroundTransitionLayer.style.backgroundImage = `url('${newArtUrl}')`;
|
backgroundTransitionLayer.style.backgroundImage = `url('${newArtUrl}')`;
|
||||||
|
|||||||
@@ -115,14 +115,6 @@
|
|||||||
text-align: var(--text-alignment);
|
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 {
|
#progress-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -151,14 +143,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #time-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
font-size: 0.7em;
|
|
||||||
opacity: 0.5;
|
|
||||||
display: none;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.progress-text {
|
.progress-text {
|
||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
|||||||
Reference in New Issue
Block a user