fix(lyrics): stop the LRC box pre-highlighting the last line at t=0
Deploy / build (push) Successful in 28s

findIndex(t > now) - 1 returned -1 before the first line started, which the
`if (i < 0) i = lines.length - 1` guard turned into the LAST line — so playback
began scrolled to the bottom. Use a last-passed-index scan instead (-1 = before
the first line), centre the active line inside the box (so upcoming lyrics are
visible), and scroll the box itself rather than scrollIntoView(), which dragged
the whole page. Also bumps the box to 250px. Fixes yue2 post + both Suno posts.
This commit is contained in:
hoelee committed 2026-10-20 21:00:29 +08:00
1 parent 7a9906ec96
commit 6e2a55fe34
4 files changed
+52 -16

No files matched your search

@@ -88,7 +88,7 @@ Publitio) and embedded with plain HTML5 audio. Press play:
<style>
.suno-track{margin:2.2rem 0}
.suno-track audio{width:100%;max-width:560px}
.suno-box{max-height:190px;overflow-y:auto;border:1px solid var(--border,#3a3f47);border-radius:8px;padding:.7rem 1rem;margin-top:.6rem;font-size:.95em;line-height:1.6}
.suno-box{max-height:250px;overflow-y:auto;border:1px solid var(--border,#3a3f47);border-radius:8px;padding:.7rem 1rem;margin-top:.6rem;font-size:.95em;line-height:1.6}
.suno-box p{margin:.18rem 0;opacity:.55}
.suno-box p.on{opacity:1;font-weight:600;color:var(--accent,#22c55e)}
</style>
@@ -287,12 +287,21 @@ document.querySelectorAll('audio[data-lrc]').forEach((audio) => {
let cur = -1;
audio.addEventListener('timeupdate', () => {
const now = audio.currentTime;
let i = lines.findIndex((l) => l.t > now) - 1;
if (i < 0) i = lines.length - 1;
// last line whose timestamp has passed; -1 before the first line starts
// (findIndex()-1 went negative at t=0 and wrapped to the LAST line)
let i = -1;
for (let k = 0; k < lines.length; k++) { if (lines[k].t <= now) i = k; else break; }
if (i !== cur) {
if (cur >= 0) box.children[cur].classList.remove('on');
cur = i;
if (cur >= 0) { box.children[cur].classList.add('on'); box.children[cur].scrollIntoView({ block: 'nearest' }); }
if (cur >= 0) {
const p = box.children[cur];
p.classList.add('on');
// scroll the lyric box only (scrollIntoView would drag the whole page)
box.scrollTo({ top: p.offsetTop - box.clientHeight / 2 + p.offsetHeight / 2, behavior: 'smooth' });
} else {
box.scrollTop = 0; // back to the top before the first line
}
}
});
});
@@ -227,7 +227,7 @@ against the written lyrics, since the model emits no timestamps.
<style>
.yue2-track{margin:2.2rem 0}
.yue2-track audio{width:100%;max-width:560px}
.yue2-box{max-height:190px;overflow-y:auto;border:1px solid var(--border,#3a3f47);border-radius:8px;padding:.7rem 1rem;margin-top:.6rem;font-size:.95em;line-height:1.6}
.yue2-box{max-height:250px;overflow-y:auto;border:1px solid var(--border,#3a3f47);border-radius:8px;padding:.7rem 1rem;margin-top:.6rem;font-size:.95em;line-height:1.6}
.yue2-box p{margin:.18rem 0;opacity:.55}
.yue2-box p.on{opacity:1;font-weight:600;color:var(--accent,#22c55e)}
</style>
@@ -951,12 +951,21 @@ document.querySelectorAll('audio[data-lrc]').forEach((audio) => {
let cur = -1;
audio.addEventListener('timeupdate', () => {
const now = audio.currentTime;
let i = lines.findIndex((l) => l.t > now) - 1;
if (i < 0) i = lines.length - 1;
// last line whose timestamp has passed; -1 before the first line starts
// (findIndex()-1 went negative at t=0 and wrapped to the LAST line)
let i = -1;
for (let k = 0; k < lines.length; k++) { if (lines[k].t <= now) i = k; else break; }
if (i !== cur) {
if (cur >= 0) box.children[cur].classList.remove('on');
cur = i;
if (cur >= 0) { box.children[cur].classList.add('on'); box.children[cur].scrollIntoView({ block: 'nearest' }); }
if (cur >= 0) {
const p = box.children[cur];
p.classList.add('on');
// scroll the lyric box only (scrollIntoView would drag the whole page)
box.scrollTo({ top: p.offsetTop - box.clientHeight / 2 + p.offsetHeight / 2, behavior: 'smooth' });
} else {
box.scrollTop = 0; // back to the top before the first line
}
}
});
});
@@ -74,7 +74,7 @@ v6 家族新增的控制功能,直接改变了我做事的方式:
<style>
.suno-track{margin:2.2rem 0}
.suno-track audio{width:100%;max-width:560px}
.suno-box{max-height:190px;overflow-y:auto;border:1px solid var(--border,#3a3f47);border-radius:8px;padding:.7rem 1rem;margin-top:.6rem;font-size:.95em;line-height:1.6}
.suno-box{max-height:250px;overflow-y:auto;border:1px solid var(--border,#3a3f47);border-radius:8px;padding:.7rem 1rem;margin-top:.6rem;font-size:.95em;line-height:1.6}
.suno-box p{margin:.18rem 0;opacity:.55}
.suno-box p.on{opacity:1;font-weight:600;color:var(--accent,#22c55e)}
</style>
@@ -266,12 +266,21 @@ document.querySelectorAll('audio[data-lrc]').forEach((audio) => {
let cur = -1;
audio.addEventListener('timeupdate', () => {
const now = audio.currentTime;
let i = lines.findIndex((l) => l.t > now) - 1;
if (i < 0) i = lines.length - 1;
// last line whose timestamp has passed; -1 before the first line starts
// (findIndex()-1 went negative at t=0 and wrapped to the LAST line)
let i = -1;
for (let k = 0; k < lines.length; k++) { if (lines[k].t <= now) i = k; else break; }
if (i !== cur) {
if (cur >= 0) box.children[cur].classList.remove('on');
cur = i;
if (cur >= 0) { box.children[cur].classList.add('on'); box.children[cur].scrollIntoView({ block: 'nearest' }); }
if (cur >= 0) {
const p = box.children[cur];
p.classList.add('on');
// scroll the lyric box only (scrollIntoView would drag the whole page)
box.scrollTo({ top: p.offsetTop - box.clientHeight / 2 + p.offsetHeight / 2, behavior: 'smooth' });
} else {
box.scrollTop = 0; // back to the top before the first line
}
}
});
});
@@ -172,7 +172,7 @@ clean = "\n".join(l for l in raw.split("\n")
<style>
.yue2-track{margin:2.2rem 0}
.yue2-track audio{width:100%;max-width:560px}
.yue2-box{max-height:190px;overflow-y:auto;border:1px solid var(--border,#3a3f47);border-radius:8px;padding:.7rem 1rem;margin-top:.6rem;font-size:.95em;line-height:1.6}
.yue2-box{max-height:250px;overflow-y:auto;border:1px solid var(--border,#3a3f47);border-radius:8px;padding:.7rem 1rem;margin-top:.6rem;font-size:.95em;line-height:1.6}
.yue2-box p{margin:.18rem 0;opacity:.55}
.yue2-box p.on{opacity:1;font-weight:600;color:var(--accent,#22c55e)}
</style>
@@ -896,12 +896,21 @@ document.querySelectorAll('audio[data-lrc]').forEach((audio) => {
let cur = -1;
audio.addEventListener('timeupdate', () => {
const now = audio.currentTime;
let i = lines.findIndex((l) => l.t > now) - 1;
if (i < 0) i = lines.length - 1;
// last line whose timestamp has passed; -1 before the first line starts
// (findIndex()-1 went negative at t=0 and wrapped to the LAST line)
let i = -1;
for (let k = 0; k < lines.length; k++) { if (lines[k].t <= now) i = k; else break; }
if (i !== cur) {
if (cur >= 0) box.children[cur].classList.remove('on');
cur = i;
if (cur >= 0) { box.children[cur].classList.add('on'); box.children[cur].scrollIntoView({ block: 'nearest' }); }
if (cur >= 0) {
const p = box.children[cur];
p.classList.add('on');
// scroll the lyric box only (scrollIntoView would drag the whole page)
box.scrollTo({ top: p.offsetTop - box.clientHeight / 2 + p.offsetHeight / 2, behavior: 'smooth' });
} else {
box.scrollTop = 0; // back to the top before the first line
}
}
});
});