mirror of
https://github.com/nuttylmao/nutty.gg.git
synced 2026-09-18 19:50:58 -04:00
• Updated Marquee animation — should be more consistent
• Updated Settings core — Streamer.bot will not load in the background for widgets that do not require it
This commit is contained in:
@@ -337,6 +337,7 @@ function RefreshWidgetPreview() {
|
||||
}
|
||||
|
||||
function UpdateStreamerBotConnection() {
|
||||
// If Streamer.bot is not being used, exit early
|
||||
if (!useStreamerBot)
|
||||
return;
|
||||
|
||||
@@ -359,6 +360,8 @@ function UpdateStreamerBotConnection() {
|
||||
//////////////////
|
||||
|
||||
// Connect to Streamer.bot and get list of actions
|
||||
if (useStreamerBot)
|
||||
{
|
||||
let sbServerAddress = '127.0.0.1';
|
||||
let sbServerPort = '8080';
|
||||
let client = new StreamerbotClient({
|
||||
@@ -394,6 +397,7 @@ async function GetSBActions() {
|
||||
|
||||
document.body.appendChild(datalistElement);
|
||||
}
|
||||
}
|
||||
|
||||
async function PopulateFontDatalist() {
|
||||
if (!('queryLocalFonts' in window)) {
|
||||
|
||||
+48
-12
@@ -475,9 +475,6 @@ function SetVisibility(visible) {
|
||||
// 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;
|
||||
@@ -495,8 +492,11 @@ function SetLabelText(elementId, text) {
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust this value to change scroll speed (pixels per second)
|
||||
const SCROLL_SPEED_PX_PER_SEC = 30;
|
||||
const PAUSE_SEC = 5;
|
||||
|
||||
function ApplyMarquee(label, text) {
|
||||
// 1. Render single span to measure dimensions
|
||||
label.classList.remove('is-overflowing');
|
||||
label.innerHTML = `<span class="scroll-content"></span>`;
|
||||
label.querySelector('.scroll-content').textContent = text;
|
||||
@@ -507,19 +507,55 @@ function ApplyMarquee(label, text) {
|
||||
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;
|
||||
// --- CONSTANT TIMINGS ---
|
||||
const travelSec = overflowDistance / SCROLL_SPEED_PX_PER_SEC;
|
||||
const pauseSec = PAUSE_SEC; // Always constant at each end
|
||||
|
||||
const totalSec = (travelSec * 2) + (pauseSec * 2);
|
||||
const totalMs = totalSec * 1000;
|
||||
|
||||
const p1 = (pauseSec / totalSec);
|
||||
const p2 = ((pauseSec + travelSec) / totalSec);
|
||||
const p3 = ((pauseSec * 2 + travelSec) / totalSec);
|
||||
|
||||
// Pass calculated variables to CSS
|
||||
scrollContent.style.setProperty('--scroll-distance', `-${distancePercent}%`);
|
||||
label.style.setProperty('--marquee-duration', `${totalDuration}s`);
|
||||
|
||||
// 1. Animate Text Translation
|
||||
scrollContent.animate([
|
||||
// Stay at start during pause 1 (linear/instant transition into movement)
|
||||
{ transform: 'translateX(0%)', offset: 0, easing: 'linear' },
|
||||
{ transform: 'translateX(0%)', offset: p1, easing: 'ease-in-out' }, // <--- Easing starts HERE as it leaves the edge
|
||||
|
||||
// Travel to the far end with smooth ease-in-out
|
||||
{ transform: `translateX(-${distancePercent}%)`, offset: p2, easing: 'linear' }, // <--- Easing ends HERE as it arrives
|
||||
|
||||
// Stay at far end during pause 2
|
||||
{ transform: `translateX(-${distancePercent}%)`, offset: p3, easing: 'ease-in-out' }, // <--- Easing starts HERE for return trip
|
||||
|
||||
// Return to start with smooth ease-in-out
|
||||
{ transform: 'translateX(0%)', offset: 1, easing: 'linear' } // <--- Easing ends HERE back at start
|
||||
], {
|
||||
duration: totalMs,
|
||||
iterations: Infinity
|
||||
});
|
||||
|
||||
// 2. Animate Mask Variables simultaneously to keep soft edges synced
|
||||
label.animate([
|
||||
{ '--mask-left': 'black', '--mask-right': 'transparent', offset: 0 },
|
||||
{ '--mask-left': 'black', '--mask-right': 'transparent', offset: p1 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'transparent', offset: p1 + 0.02 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'transparent', offset: p2 - 0.02 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'black', offset: p2 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'black', offset: p3 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'transparent', offset: p3 + 0.02 },
|
||||
{ '--mask-left': 'black', '--mask-right': 'transparent', offset: 1 }
|
||||
], {
|
||||
duration: totalMs,
|
||||
iterations: Infinity
|
||||
});
|
||||
|
||||
label.classList.add('is-overflowing');
|
||||
}
|
||||
|
||||
@@ -183,13 +183,7 @@ body {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.text-label.is-overflowing .scroll-content {
|
||||
animation: ping-pong-scroll var(--marquee-duration, 8s) ease-in-out infinite;
|
||||
}
|
||||
|
||||
.text-label.is-overflowing {
|
||||
animation: ping-pong-mask var(--marquee-duration, 8s) ease-in-out infinite;
|
||||
|
||||
/* Static gradient structure referencing dynamic color variables */
|
||||
mask-image: linear-gradient(
|
||||
to right,
|
||||
@@ -206,47 +200,3 @@ body {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,6 @@ function UpdateSingleSongLabel(containerId, trackName, artistName) {
|
||||
|
||||
let scrollContent = container.querySelector('.scroll-content');
|
||||
|
||||
// Only rebuild the HTML structure if the text content actually changed or doesn't exist yet
|
||||
const currentTrackEl = container.querySelector('.track-label');
|
||||
const currentArtistEl = container.querySelector('.artist-label');
|
||||
|
||||
@@ -160,7 +159,6 @@ function UpdateSingleSongLabel(containerId, trackName, artistName) {
|
||||
scrollContent = container.querySelector('.scroll-content');
|
||||
}
|
||||
|
||||
// Encapsulate measurement logic so it can be called safely by the observer
|
||||
const updateMetrics = () => {
|
||||
scrollContent = container.querySelector('.scroll-content');
|
||||
|
||||
@@ -170,20 +168,61 @@ function UpdateSingleSongLabel(containerId, trackName, artistName) {
|
||||
|
||||
if (overflowDistance > 0) {
|
||||
const distancePercent = (overflowDistance / textWidth) * 100;
|
||||
const travelTime = overflowDistance / SCROLL_SPEED_PX_PER_SEC;
|
||||
const totalDuration = (travelTime * 2) + 3;
|
||||
|
||||
container.style.setProperty('--scroll-distance', `-${distancePercent}%`);
|
||||
container.style.setProperty('--marquee-duration', `${totalDuration}s`);
|
||||
// --- CONSTANT TIMINGS ---
|
||||
const travelSec = overflowDistance / SCROLL_SPEED_PX_PER_SEC;
|
||||
const pauseSec = PAUSE_SEC;
|
||||
|
||||
const totalSec = (travelSec * 2) + (pauseSec * 2);
|
||||
const totalMs = totalSec * 1000;
|
||||
|
||||
const p1 = (pauseSec / totalSec);
|
||||
const p2 = ((pauseSec + travelSec) / totalSec);
|
||||
const p3 = ((pauseSec * 2 + travelSec) / totalSec);
|
||||
|
||||
scrollContent.style.setProperty('--scroll-distance', `-${distancePercent}%`);
|
||||
|
||||
// Clear any previous active animations to prevent stacking on resize
|
||||
if (scrollContent._anim) scrollContent._anim.cancel();
|
||||
if (container._anim) container._anim.cancel();
|
||||
|
||||
// 1. Animate Text Translation inside container
|
||||
scrollContent._anim = scrollContent.animate([
|
||||
{ transform: 'translateX(0%)', offset: 0, easing: 'linear' },
|
||||
{ transform: 'translateX(0%)', offset: p1, easing: 'ease-in-out' },
|
||||
{ transform: `translateX(-${distancePercent}%)`, offset: p2, easing: 'linear' },
|
||||
{ transform: `translateX(-${distancePercent}%)`, offset: p3, easing: 'ease-in-out' },
|
||||
{ transform: 'translateX(0%)', offset: 1, easing: 'linear' }
|
||||
], {
|
||||
duration: totalMs,
|
||||
iterations: Infinity
|
||||
});
|
||||
|
||||
// 2. Animate Mask Variables simultaneously on the container
|
||||
container._anim = container.animate([
|
||||
{ '--mask-left': 'black', '--mask-right': 'transparent', offset: 0 },
|
||||
{ '--mask-left': 'black', '--mask-right': 'transparent', offset: p1 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'transparent', offset: p1 + 0.02 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'transparent', offset: p2 - 0.02 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'black', offset: p2 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'black', offset: p3 },
|
||||
{ '--mask-left': 'transparent', '--mask-right': 'transparent', offset: p3 + 0.02 },
|
||||
{ '--mask-left': 'black', '--mask-right': 'transparent', offset: 1 }
|
||||
], {
|
||||
duration: totalMs,
|
||||
iterations: Infinity
|
||||
});
|
||||
|
||||
container.classList.add('is-overflowing');
|
||||
} else {
|
||||
container.classList.remove('is-overflowing');
|
||||
if (scrollContent._anim) scrollContent._anim.cancel();
|
||||
if (container._anim) container._anim.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
updateMetrics();
|
||||
|
||||
// Attach ResizeObserver once to update metrics only (No recursive function resets!)
|
||||
if (!container._resizeObserver) {
|
||||
container._resizeObserver = new ResizeObserver(() => {
|
||||
updateMetrics();
|
||||
|
||||
@@ -88,15 +88,3 @@
|
||||
opacity: 0.7;
|
||||
display: var(--show-secondary);
|
||||
}
|
||||
|
||||
/* Marquee scroll animation */
|
||||
#song-label.is-overflowing .scroll-content,
|
||||
#song-label-background.is-overflowing .scroll-content {
|
||||
animation: ping-pong-scroll var(--marquee-duration, 8s) ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Mask animation applied to container */
|
||||
#song-label.is-overflowing,
|
||||
#song-label-background.is-overflowing {
|
||||
animation: ping-pong-mask var(--marquee-duration, 8s) ease-in-out infinite;
|
||||
}
|
||||
Reference in New Issue
Block a user