带渐变文字的无限滚动跑马灯Safari卡顿重叠问题排查及替代方案咨询
我完全懂你现在的困扰——这个带渐变文字的无限跑马灯在其他浏览器都好好的,到Safari就卡得不行还出现文字重叠,太影响体验了。咱们先看看你的现有实现,再一步步聊优化思路和替代方案。
你的当前实现代码
先把你提供的代码整理好,方便对照:
HTML
<div class="running-lines"> <div class="running-lines__wrapper" data-duration="28000" data-duplicate-count="7"> <p class="running-lines__items"> <span class="running-lines__item">Security Kit</span> <span class="running-lines__item">Honeypot</span> <span class="running-lines__item">Antibot</span> <span class="running-lines__item">Flood Control</span> <span class="running-lines__item">Move 403 to 404</span> <span class="running-lines__item">Rabbit Hole</span> <span class="running-lines__item">Username Enumeration Prevention</span> </p> </div> <div class="running-lines__wrapper" data-duration="50000" data-direction="reverse" data-duplicate-count="7"> <p class="running-lines__items"> <span class="running-lines__item">Content Moderation</span> <span class="running-lines__item">Workflows</span> <span class="running-lines__item">Simple Menu Permissions</span> <span class="running-lines__item">Group Node</span> <span class="running-lines__item">Scheduler</span> <span class="running-lines__item">Media</span> </p> </div> <div class="running-lines__wrapper" data-duration="40000" data-duplicate-count="7"> <p class="running-lines__items"> <span class="running-lines__item">Paragraphs & Paragraphs Browser</span> <span class="running-lines__item">Group</span> <span class="running-lines__item">Gin & Gin Toolbar</span> <span class="running-lines__item">Crop & Image Widget Crop</span> <span class="running-lines__item">Migrate & Migrate Plus</span> </p> </div> </div>
CSS
.running-lines { font-family: "Poppins"; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; row-gap: 0.5rem; overflow: hidden; background-image: linear-gradient(92.55deg, #852fff 0%, #dd26ed 48.5%, #ed2662 100%); -webkit-background-clip: text; background-clip: text; padding-top: 4rem; padding-bottom: 4rem; mix-blend-mode: lighten; } @media (min-width: 956px) { .running-lines { row-gap: 1rem; padding-top: 3.5rem; padding-bottom: 3.5rem; } } @media (min-width: 1184px) { .running-lines { row-gap: 1.25rem; } } .running-lines__wrapper { display: -webkit-box; display: -ms-flexbox; display: flex; padding-top: 0.625rem; padding-bottom: 0.625rem; } .running-lines__items { margin-bottom: 0; text-wrap: nowrap; will-change: text-indent; /* animation: marquee 15s linear infinite; */ } .running-lines__item { margin-right: 2rem; font-size: 1.25rem; line-height: 1.5rem; font-style: italic; color: transparent; } @media (min-width: 956px) { .running-lines__item { margin-right: 3rem; font-size: 1.5rem; line-height: 2rem; } } @media (min-width: 1184px) { .running-lines__item { margin-right: 4rem; } } @keyframes marquee { from { text-indent: 0; } to { text-indent: -100%; } }
JavaScript
document.querySelectorAll('.running-lines__wrapper').forEach( (line) => { const content = line.innerHTML; const duplicateCount = line.getAttribute('data-duplicate-count') || 1; const duration = parseInt(line.getAttribute('data-duration'), 10) || 10000; const direction = line.getAttribute('data-direction') || 'normal'; let duplicatedContent = ''; for (let i = 0; i < duplicateCount; i++) { duplicatedContent += content; } line.innerHTML = duplicatedContent; const lineText = line.querySelectorAll('.running-lines__items'); lineText.forEach((text) => { const fromFrame = { textIndent: 0 }; const toFrame = { textIndent: '-100%' }; const options = { duration, iterations: Infinity, direction }; text.animate([fromFrame, toFrame], options); }); });
问题根源分析
Safari出现卡顿和重叠主要有这几个原因:
- Web Animations API性能问题:你用的
element.animate()在Safari里对text-indent这类布局属性的动画支持不够流畅,尤其是结合background-clip: text的渐变文字时,渲染开销会大幅增加,容易掉帧。 text-indent计算差异:Safari对text-indent: -100%的解析和其他浏览器有细微差别,加上重复内容过多,容易导致文字重叠。- 渲染叠加开销:
mix-blend-mode: lighten和渐变文字组合,会让Safari的渲染引擎额外计算图层混合,进一步降低性能。
针对Safari的优化方案
1. 替换Web Animations API为CSS动画
你已经写好了@keyframes marquee,直接用CSS的animation属性代替JS的动画创建,Safari对CSS动画的兼容性和性能要好很多。
修改步骤:
- 调整JS代码,去掉动画创建部分,改为给元素添加样式:
document.querySelectorAll('.running-lines__wrapper').forEach( (line) => { const content = line.innerHTML; const duplicateCount = line.getAttribute('data-duplicate-count') || 1; const duration = parseInt(line.getAttribute('data-duration'), 10) || 10000; const direction = line.getAttribute('data-direction') || 'normal'; let duplicatedContent = ''; for (let i = 0; i < duplicateCount; i++) { duplicatedContent += content; } line.innerHTML = duplicatedContent; const lineText = line.querySelectorAll('.running-lines__items'); lineText.forEach((text) => { // 用CSS动画替代JS动画 text.style.animation = `marquee ${duration}ms linear infinite ${direction}`; }); });
- 去掉CSS中
.running-lines__items的will-change: text-indent(换成CSS动画后不需要了),或者改成will-change: transform(后续优化会用到)。
2. 优化渐变文字渲染
在.running-lines__item里添加Safari专属的渲染优化属性:
.running-lines__item { /* 原有属性不变 */ -webkit-font-smoothing: antialiased; -webkit-text-stroke: 0.5px transparent; }
如果mix-blend-mode: lighten不是必须的,建议去掉它,能大幅减少渲染压力。
3. 减少重复内容的数量
data-duplicate-count="7"太多了,会导致DOM内容过长。你可以计算刚好覆盖视口2倍宽度的重复次数,比如改成2-3次,既能实现无限滚动,又不会增加不必要的渲染负担。
更稳定的替代方案:用GPU加速的Transform实现
transform: translateX()是GPU加速的属性,在Safari里性能远优于text-indent,而且不会出现文字重叠问题。实现思路是把跑马灯内容复制一份,用flex布局排列,然后通过transform: translateX(-50%)实现无限滚动。
示例代码(修改版)
HTML
<div class="running-lines"> <div class="running-lines__wrapper" data-duration="28000" data-direction="normal"> <div class="running-lines__track"> <div class="running-lines__items"> <span class="running-lines__item">Security Kit</span> <span class="running-lines__item">Honeypot</span> <span class="running-lines__item">Antibot</span> <span class="running-lines__item">Flood Control</span> <span class="running-lines__item">Move 403 to 404</span> <span class="running-lines__item">Rabbit Hole</span> <span class="running-lines__item">Username Enumeration Prevention</span> </div> <!-- 复制一份内容,实现无缝滚动 --> <div class="running-lines__items"> <span class="running-lines__item">Security Kit</span> <span class="running-lines__item">Honeypot</span> <span class="running-lines__item">Antibot</span> <span class="running-lines__item">Flood Control</span> <span class="running-lines__item">Move 403 to 404</span> <span class="running-lines__item">Rabbit Hole</span> <span class="running-lines__item">Username Enumeration Prevention</span> </div> </div> </div> <!-- 其他行类似,复制内容即可 --> </div>
CSS
.running-lines { /* 原有属性不变,去掉mix-blend-mode: lighten(可选) */ } .running-lines__wrapper { overflow: hidden; /* 隐藏超出部分 */ padding-top: 0.625rem; padding-bottom: 0.625rem; } .running-lines__track { display: flex; will-change: transform; /* 告诉浏览器提前准备GPU加速 */ } .running-lines__items { display: flex; gap: 2rem; /* 替代margin-right,更灵活 */ flex-shrink: 0; /* 防止内容被压缩 */ } .running-lines__item { /* 原有属性不变,去掉margin-right */ } /* 动画改为transform */ @keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-100%); } } @keyframes marquee-reverse { from { transform: translateX(-100%); } to { transform: translateX(0); } }
JavaScript
document.querySelectorAll('.running-lines__wrapper').forEach( (line) => { const duration = parseInt(line.getAttribute('data-duration'), 10) || 10000; const direction = line.getAttribute('data-direction') || 'normal'; const track = line.querySelector('.running-lines__track'); // 根据方向选择动画 const animationName = direction === 'reverse' ? 'marquee-reverse' : 'marquee'; track.style.animation = `${animationName} ${duration}ms linear infinite`; });
这个方案用GPU加速的transform,在Safari里几乎不会卡顿,而且因为复制了完整的内容,不会出现文字重叠的问题,是目前最稳定的实现方式。
总结
优先尝试把Web Animations API换成CSS动画,结合渐变文字的渲染优化;如果还是有问题,就改用transform的方案,这个在Safari里的兼容性和性能都更可靠。
备注:内容来源于stack exchange,提问作者Clop1986

