diff --git a/frontend/site.js b/frontend/site.js index 3dae327..b8d636b 100644 --- a/frontend/site.js +++ b/frontend/site.js @@ -109,13 +109,14 @@ function createRow(entries, rowIndex) { const row = document.createElement("div"); row.className = "tab-row"; - const speed = 90 + (rng() * 60); // 90-150s per cycle + const pxPerSec = 20 + (rng() * 15); // 20-35 px/sec, consistent across screen sizes const goLeft = rng() > 0.5; const stagger = rng(); - row.style.setProperty("--speed", `${speed}s`); row.classList.add(goLeft ? "scroll-left" : "scroll-right"); - row.style.animationDelay = `${-stagger * speed}s`; + + // Store config — duration calculated after DOM insertion (needs measured width) + row._animConfig = { pxPerSec, stagger }; // Add tabs twice so the marquee loops seamlessly (translate -50% = one full set) for (let copy = 0; copy < 2; copy++) { @@ -127,6 +128,15 @@ function createRow(entries, rowIndex) { return row; } +// Set animation speed based on actual row width (must be called after DOM insertion) +function startRowAnimation(row) { + const { pxPerSec, stagger } = row._animConfig; + const halfWidth = row.scrollWidth / 2; + const duration = halfWidth / pxPerSec; + row.style.setProperty("--speed", `${duration}s`); + row.style.animationDelay = `${-stagger * duration}s`; +} + // Render entries into rows function renderEntries(entries) { @@ -136,7 +146,9 @@ function renderEntries(entries) { for (let i = 0; i < entries.length; i += perRow) { const rowEntries = entries.slice(i, i + perRow); if (rowEntries.length < 3) continue; // skip tiny last rows - container.appendChild(createRow(rowEntries, rowIndex)); + const row = createRow(rowEntries, rowIndex); + container.appendChild(row); + startRowAnimation(row); rowIndex++; } }