fix(lyrics): centre the active line with rect deltas, not offsetTop
Deploy / build (push) Successful in 42s
Deploy / build (push) Successful in 42s
offsetTop is measured from the nearest positioned ancestor, so inside an unpositioned .yue2-box it returned the line's distance from the page top (thousands of px). scrollTop clamped that to maxScroll, so the box jumped to the bottom the moment singing started and the text was unreadable. Now: box.scrollTop += (pr.top - br.top) - (clientHeight - lineHeight)/2, plus position:relative on the box. Verified in headless Chrome with a numeric probe: the active line sits ~124px into a 274px box at t=48/100, nothing is highlighted and scrollTop=0 at t=0, and the box settles at maxScroll only for the final lines.
This commit is contained in:
1 parent
6e2a55fe34
commit
cf327211df
4 files changed
+24
-12
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: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{position:relative;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>
|
||||
@@ -297,8 +297,11 @@ document.querySelectorAll('audio[data-lrc]').forEach((audio) => {
|
||||
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' });
|
||||
// centre the active line inside the box. Use rect deltas, NOT offsetTop:
|
||||
// offsetTop is measured from the nearest *positioned* ancestor, so without
|
||||
// position:relative on the box it is huge and scrollTop clamps to the bottom.
|
||||
const br = box.getBoundingClientRect(), pr = p.getBoundingClientRect();
|
||||
box.scrollTop += (pr.top - br.top) - (box.clientHeight - pr.height) / 2;
|
||||
} 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: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{position:relative;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>
|
||||
@@ -961,8 +961,11 @@ document.querySelectorAll('audio[data-lrc]').forEach((audio) => {
|
||||
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' });
|
||||
// centre the active line inside the box. Use rect deltas, NOT offsetTop:
|
||||
// offsetTop is measured from the nearest *positioned* ancestor, so without
|
||||
// position:relative on the box it is huge and scrollTop clamps to the bottom.
|
||||
const br = box.getBoundingClientRect(), pr = p.getBoundingClientRect();
|
||||
box.scrollTop += (pr.top - br.top) - (box.clientHeight - pr.height) / 2;
|
||||
} 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: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{position:relative;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>
|
||||
@@ -276,8 +276,11 @@ document.querySelectorAll('audio[data-lrc]').forEach((audio) => {
|
||||
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' });
|
||||
// centre the active line inside the box. Use rect deltas, NOT offsetTop:
|
||||
// offsetTop is measured from the nearest *positioned* ancestor, so without
|
||||
// position:relative on the box it is huge and scrollTop clamps to the bottom.
|
||||
const br = box.getBoundingClientRect(), pr = p.getBoundingClientRect();
|
||||
box.scrollTop += (pr.top - br.top) - (box.clientHeight - pr.height) / 2;
|
||||
} 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: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{position:relative;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>
|
||||
@@ -906,8 +906,11 @@ document.querySelectorAll('audio[data-lrc]').forEach((audio) => {
|
||||
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' });
|
||||
// centre the active line inside the box. Use rect deltas, NOT offsetTop:
|
||||
// offsetTop is measured from the nearest *positioned* ancestor, so without
|
||||
// position:relative on the box it is huge and scrollTop clamps to the bottom.
|
||||
const br = box.getBoundingClientRect(), pr = p.getBoundingClientRect();
|
||||
box.scrollTop += (pr.top - br.top) - (box.clientHeight - pr.height) / 2;
|
||||
} else {
|
||||
box.scrollTop = 0; // back to the top before the first line
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user