You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在元素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拿的是新的空变量,根本清不掉之前的定时器。现在用sheetStates Map存储每个sheet的定时器实例,确保能正确清除。
  • 保存动画进度:用currentIndex记录每个sheet当前显示到第几个字符,重新进入视口时可以接着之前的进度继续,也可以通过注释的代码重置为从头开始。
  • 避免重复创建定时器:进入视口时先检查定时器是否已存在,防止多次触发观察者回调导致多个定时器同时运行,让动画加速。
  • 自动清理完成后的定时器:当所有字符都显示完成后,自动清除定时器,避免无用的定时器占用资源。

内容的提问来源于stack exchange,提问作者ImEmo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 16:23:13