如何使用Vanilla JS实现滚动时文本逐行显示的效果
滚动逐行显示文本实现方案
1. 原生JS实现行元素包裹span
针对你给出的带<br>硬换行的文本场景,实现代码如下:
// 选中所有需要处理的文本段落 const paragraphs = document.querySelectorAll('p'); paragraphs.forEach(p => { // 按<br>标签分割内容,过滤空行 const lines = p.innerHTML.split(/<br\s*\/?>/i).filter(line => line.trim()); // 拼接生成带span包裹的新内容 p.innerHTML = lines.map(line => `<span class="line">${line.trim()}<br></span>`).join(''); });
执行后即可得到你期望的每行内容独立包裹span的结构。
如果是没有硬写<br>、由容器宽度自动换行的文本,可以用Range API实现行拆分,示例代码如下:
function wrapAutoLines(el) { const text = el.textContent; el.innerHTML = ''; const words = text.split(' '); let currentLine = ''; // 临时元素用于计算行宽 const testSpan = document.createElement('span'); testSpan.style.visibility = 'hidden'; el.appendChild(testSpan); words.forEach(word => { const testContent = currentLine + word + ' '; testSpan.textContent = testContent; // 行宽超出容器时拆分新行 if (testSpan.offsetWidth > el.offsetWidth && currentLine) { const lineSpan = document.createElement('span'); lineSpan.className = 'line'; lineSpan.textContent = currentLine; el.appendChild(lineSpan); currentLine = word + ' '; } else { currentLine = testContent; } }); // 插入最后一行 const lastLine = document.createElement('span'); lastLine.className = 'line'; lastLine.textContent = currentLine; el.appendChild(lastLine); el.removeChild(testSpan); }
2. 滚动触发逐行显示效果
采用性能更优的IntersectionObserver实现,无需监听滚动事件,代码如下:
样式代码
.line { /* 初始隐藏状态 */ opacity: 0; transform: translateY(15px); /* 过渡动画配置 */ transition: all 0.5s cubic-bezier(0.22, 1, 0.36, 1); /* 自动换行场景需要开启block属性 */ display: block; margin-bottom: 0.2em; } .line.show { opacity: 1; transform: translateY(0); }
逻辑代码
// 创建元素观察者 const lineObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { // 行元素进入视口时触发显示 if (entry.isIntersecting) { entry.target.classList.add('show'); // 已显示的元素取消监听减少性能消耗 lineObserver.unobserve(entry.target); } }); }, { // 元素露出10%时触发动画 threshold: 0.1, // 可调整rootMargin实现提前触发效果 rootMargin: '0px 0px -50px 0px' }); // 给所有行元素绑定监听,如需依次延迟显示可加递增的transition-delay document.querySelectorAll('.line').forEach((line, index) => { // 每一行延迟0.1s依次显示,不需要可以删掉下一行 line.style.transitionDelay = `${index * 0.1}s`; lineObserver.observe(line); });
内容的提问来源于stack exchange,提问作者Gg Wp
相关产品推荐
相关产品推荐

