定时器与动画帧
定时器控制延时执行,动画帧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();
定时器对比
| 特性 | setTimeout | setInterval |
|---|---|---|
| 执行次数 | 单次 | 重复 |
| 清除方法 | clearTimeout | clearInterval |
| 返回值 | timerId | timerId |
| 精度 | 约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);
| 特性 | requestAnimationFrame | setInterval |
|---|---|---|
| 帧率 | 自动适配屏幕刷新率 | 固定间隔 |
| 流畅性 | 与屏幕同步 | 可能卡顿 |
| 节能 | 页面隐藏时暂停 | 继续执行 |
| 精度 | 高精度时间戳 | 固定延时 |
匀速动画示例
基于时间戳
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);
}
});
要点总结
- setTimeout单次:延时执行一次
- setInterval递归替代:避免执行时间累积
- requestAnimationFrame动画:流畅、节能、同步屏幕
- 清除定时器:组件销毁时必须清除
- 防抖节流:避免重复触发累积定时器
📝 发现内容有误?点击此处直接编辑