全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-17 8 分钟 ✍️ juanwangdev

定时器与动画帧

定时器控制延时执行,动画帧API实现流畅动画效果。

setTimeout

基本用法

JavaScript
// 延时执行
setTimeout(() => {
    console.log('1秒后执行');
}, 1000);

// 带参数传递
setTimeout((name, age) => {
    console.log(name, age);
}, 1000, '张三', 25);

// 返回定时器ID
const timerId = setTimeout(() => {}, 1000);

清除定时器

JavaScript
const timerId = setTimeout(() => {
    console.log('执行');
}, 5000);

// 取消定时器
clearTimeout(timerId);

立即执行

JavaScript
// 最小延时(实际约4ms)
setTimeout(() => {}, 0);

// 等同于(加入宏任务队列)
setTimeout(() => {}, 1);

setInterval

基本用法

JavaScript
// 重复执行
const timerId = setInterval(() => {
    console.log('每1秒执行');
}, 1000);

// 清除定时器
clearInterval(timerId);

注意事项

JavaScript
// 问题:执行时间可能超过间隔
setInterval(() => {
    // 如果执行时间超过1秒,下一次立即开始
}, 1000);

// 解决:使用setTimeout递归
function repeat() {
    setTimeout(() => {
        console.log('执行');
        repeat();  // 执行完后再设置下一次
    }, 1000);
}
repeat();

定时器对比

特性setTimeoutsetInterval
执行次数单次重复
清除方法clearTimeoutclearInterval
返回值timerIdtimerId
精度约4ms最小约4ms最小

requestAnimationFrame

基本用法

JavaScript
// 动画帧回调
function animate() {
    // 更新动画
    element.style.left = position + 'px';
    position += 1;

    // 请求下一帧
    requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

返回帧ID

JavaScript
const frameId = requestAnimationFrame(callback);

// 取消动画帧
cancelAnimationFrame(frameId);

回调参数

JavaScript
requestAnimationFrame((timestamp) => {
    // timestamp:高精度时间戳(毫秒)
    console.log(timestamp);
});

动画帧优势

vs setInterval动画

JavaScript
// setInterval(不推荐)
setInterval(() => {
    element.style.left = position + 'px';
    position += 1;
}, 16);  // 约60fps,但可能不流畅

// requestAnimationFrame(推荐)
function animate() {
    element.style.left = position + 'px';
    position += 1;
    requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
特性requestAnimationFramesetInterval
帧率自动适配屏幕刷新率固定间隔
流畅性与屏幕同步可能卡顿
节能页面隐藏时暂停继续执行
精度高精度时间戳固定延时

匀速动画示例

基于时间戳

JavaScript
function animate(timestamp) {
    if (!startTime) startTime = timestamp;

    const progress = timestamp - startTime;
    const duration = 1000;  // 1秒

    if (progress < duration) {
        element.style.left = (progress / duration * 100) + 'px';
        requestAnimationFrame(animate);
    }
}

let startTime = null;
requestAnimationFrame(animate);

缓动动画

JavaScript
function easeOut(progress) {
    return 1 - Math.pow(1 - progress, 3);
}

function animate(timestamp) {
    if (!startTime) startTime = timestamp;

    const progress = Math.min((timestamp - startTime) / duration, 1);
    const eased = easeOut(progress);

    element.style.left = (eased * 100) + 'px';

    if (progress < 1) {
        requestAnimationFrame(animate);
    }
}

定时器管理

封装定时器管理

JavaScript
class TimerManager {
    constructor() {
        this.timers = new Map();
    }

    setTimeout(key, callback, delay) {
        this.clear(key);
        const id = setTimeout(callback, delay);
        this.timers.set(key, { type: 'timeout', id });
    }

    setInterval(key, callback, interval) {
        this.clear(key);
        const id = setInterval(callback, interval);
        this.timers.set(key, { type: 'interval', id });
    }

    clear(key) {
        const timer = this.timers.get(key);
        if (timer) {
            timer.type === 'timeout' ? clearTimeout(timer.id) : clearInterval(timer.id);
            this.timers.delete(key);
        }
    }

    clearAll() {
        this.timers.forEach((timer) => {
            timer.type === 'timeout' ? clearTimeout(timer.id) : clearInterval(timer.id);
        });
        this.timers.clear();
    }
}

const timerManager = new TimerManager();
timerManager.setTimeout('delay', () => console.log('执行'), 1000);

性能优化

避免累积

JavaScript
// 问题:定时器可能累积
let timerId;
btn.addEventListener('click', () => {
    clearTimeout(timerId);  // 清除之前的
    timerId = setTimeout(() => {
        console.log('防抖执行');
    }, 300);
});

页面隐藏暂停

JavaScript
// requestAnimationFrame自动暂停
// setInterval需要手动处理
document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
        clearInterval(timerId);
    } else {
        timerId = setInterval(callback, interval);
    }
});

要点总结

  1. setTimeout单次:延时执行一次
  2. setInterval递归替代:避免执行时间累积
  3. requestAnimationFrame动画:流畅、节能、同步屏幕
  4. 清除定时器:组件销毁时必须清除
  5. 防抖节流:避免重复触发累积定时器

📝 发现内容有误?点击此处直接编辑

← 上一篇 事件处理与事件流
下一篇 → 本地存储与Cookie
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库