如何在元素isNotIntersecting时调用clearInterval()停止打字机动画?
解决打字机动画在元素离开视口后不停止的问题
我有多个<div class="sheet">元素,每个尺寸均为视口的100%。每个div内的文本字符都带有.char类,代码能在元素进入视口时触发打字机效果,但元素离开视口后动画无法停止。需求是:元素离开视口时立即停止动画,重新可见时重启动画。
原代码
const storyChars = new SplitType('.storyline', { types: 'chars' }); const sheets = document.querySelectorAll('.sheet'); function lettersReveal(entries, observer) { entries.forEach((entry) => { let autoTyper; const sheetDiv = entry.target; const letters = sheetDiv.querySelectorAll('.char'); let i = 0; console.log(entry.isIntersecting); function autoTyping() { if (i < letters.length) { letters[i].classList.add('fade') i++; } } if (entry.isIntersecting) { autoTyper = setInterval(autoTyping, 100); } else { clearInterval(autoTyper); } }); } const options = { root: null, threshold: 0.25 }; const observer = new IntersectionObserver(lettersReveal, options); sheets.forEach((sheet) => { observer.observe(sheet); });
修正后的代码
const storyChars = new SplitType('.storyline', { types: 'chars' }); const sheets = document.querySelectorAll('.sheet'); // 存储每个sheet的动画状态:定时器实例、当前字符索引 const sheetStates = new Map(); function lettersReveal(entries, observer) { entries.forEach((entry) => { const sheetDiv = entry.target; const letters = sheetDiv.querySelectorAll('.char'); // 初始化当前sheet的状态(首次触发时) let state = sheetStates.get(sheetDiv); if (!state) { state = { autoTyper: null, currentIndex: 0 }; sheetStates.set(sheetDiv, state); } function autoTyping() { if (state.currentIndex < letters.length) { letters[state.currentIndex].classList.add('fade'); state.currentIndex++; // 全部字符显示完成后自动清除定时器 if (state.currentIndex === letters.length) { clearInterval(state.autoTyper); state.autoTyper = null; } } } if (entry.isIntersecting) { // 仅当定时器未运行时启动,避免重复创建 if (!state.autoTyper) { state.autoTyper = setInterval(autoTyping, 100); } } else { // 清除当前sheet的定时器 if (state.autoTyper) { clearInterval(state.autoTyper); state.autoTyper = null; } // 可选:重置进度和字符状态,重新进入时从头开始动画 // state.currentIndex = 0; // letters.forEach(char => char.classList.remove('fade')); } }); } const options = { root: null, threshold: 0.25 }; const observer = new IntersectionObserver(lettersReveal, options); sheets.forEach((sheet) => { observer.observe(sheet); });
关键修改说明
- 修复定时器引用丢失问题:原代码中
autoTyper是回调内的局部变量,每次触发IntersectionObserver都会重新声明,导致离开视口时clearInterval拿的是新的空变量,根本清不掉之前的定时器。现在用sheetStatesMap存储每个sheet的定时器实例,确保能正确清除。 - 保存动画进度:用
currentIndex记录每个sheet当前显示到第几个字符,重新进入视口时可以接着之前的进度继续,也可以通过注释的代码重置为从头开始。 - 避免重复创建定时器:进入视口时先检查定时器是否已存在,防止多次触发观察者回调导致多个定时器同时运行,让动画加速。
- 自动清理完成后的定时器:当所有字符都显示完成后,自动清除定时器,避免无用的定时器占用资源。
内容的提问来源于stack exchange,提问作者ImEmo
相关产品推荐
相关产品推荐

