您的位置:首页 > 游戏 > 手游 > 《民用建筑通用规范》_新冠疫情实时动态_百度推广管理系统_杭州网站外包

《民用建筑通用规范》_新冠疫情实时动态_百度推广管理系统_杭州网站外包

2025/4/19 16:27:45 来源:https://blog.csdn.net/m0_55049655/article/details/147313143  浏览:    关键词:《民用建筑通用规范》_新冠疫情实时动态_百度推广管理系统_杭州网站外包
《民用建筑通用规范》_新冠疫情实时动态_百度推广管理系统_杭州网站外包

在这里插入图片描述

requestAnimationFrame 详解及与 setTimeout/setInterval 的比较

requestAnimationFrame(简称 rAF)是浏览器提供的专门用于 动画渲染 的 API,相比 setTimeoutsetInterval,它在性能和流畅度上有显著优势。以下是详细解析和对比:


1. requestAnimationFrame 详解

基本语法

const requestID = requestAnimationFrame(callback);

callback:在浏览器下一次重绘之前执行的函数。
返回值requestID(用于取消:cancelAnimationFrame(requestID))。

核心特点

  1. 与浏览器刷新率同步
    • 默认以 60Hz(16.67ms/帧) 的频率执行(匹配屏幕刷新率)。
    • 避免丢帧或过度渲染,保证动画流畅。

  2. 自动暂停后台标签页
    • 当页面隐藏或最小化时,rAF 会自动暂停,节省 CPU/GPU 资源。

  3. 高性能
    • 浏览器会优化 rAF 的调用,合并同一帧内的多次更新。

  4. 精确的时间戳参数
    • 回调函数接收一个 DOMHighResTimeStamp 参数,表示触发时间:

    requestAnimationFrame((timestamp) => {console.log(timestamp); // 精确到微秒
    });
    

示例:动画循环

function animate() {// 更新动画状态console.log("Animating...");// 循环调用requestAnimationFrame(animate);
}// 启动动画
animate();

2. requestAnimationFrame vs setTimeout/setInterval

对比维度

特性requestAnimationFramesetTimeout/setInterval
执行频率与屏幕刷新率同步(~60Hz)固定时间间隔(可能不匹配刷新率)
后台标签页行为自动暂停继续执行(浪费资源)
动画流畅度高(无丢帧)可能卡顿(因主线程阻塞或帧率不稳定)
CPU/GPU 负载低(浏览器优化)高(频繁触发回调)
适用场景动画、高频视觉更新延迟任务、低频轮询

关键差异

(1)时间精度与帧率

rAF:按屏幕刷新率(如 60Hz)执行,避免过度渲染
setTimeout(fn, 16)
• 理论上模拟 60Hz,但实际可能因主线程阻塞导致延迟。
• 浏览器最小延迟限制(4ms)可能破坏时序。

(2)资源占用

rAF:浏览器智能调度,合并帧内更新

// 连续调用 rAF 会被优化
requestAnimationFrame(animate);
requestAnimationFrame(animate); // 可能合并到同一帧

setInterval严格按间隔执行,即使前一帧未完成也可能触发新回调,导致堆积。

(3)动画示例对比
setTimeout 实现动画(不推荐)
function animate() {console.log("Animating...");setTimeout(animate, 16); // 尝试模拟 60Hz
}
animate();

问题
• 可能因主线程阻塞导致卡顿。
• 后台标签页仍执行,浪费资源。

rAF 实现动画(推荐)
function animate() {console.log("Animating...");requestAnimationFrame(animate);
}
animate();

优势
• 自动匹配刷新率,流畅且节能。
• 后台自动暂停。


3. 如何选择?

使用 requestAnimationFrame 当:

• 需要 流畅动画(如 CSS 变换、Canvas 绘图)。
• 高频更新 UI(如游戏、实时图表)。
• 希望 节省资源(特别是移动端)。

使用 setTimeout/setInterval 当:

• 需要 精确控制延迟(如 1 秒后跳转页面)。
• 执行 非视觉任务(如轮询 API)。
• 兼容旧浏览器(rAF 需 IE10+)。


4. 进阶技巧

(1)计算帧率(FPS)

let lastTime = 0;
function animate(timestamp) {const fps = 1000 / (timestamp - lastTime); // 计算帧率console.log(`FPS: ${fps.toFixed(2)}`);lastTime = timestamp;requestAnimationFrame(animate);
}
animate();

(2)降级兼容(旧浏览器)

const rAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || function(callback) {return setTimeout(callback, 16);};

(3)控制动画速度

let startTime;
function animate(timestamp) {if (!startTime) startTime = timestamp;const progress = timestamp - startTime; // 动画已运行时间const duration = 2000; // 动画总时长(2秒)if (progress < duration) {const ratio = progress / duration; // 0~1console.log(`进度: ${(ratio * 100).toFixed(1)}%`);requestAnimationFrame(animate);}
}
animate();

5. 总结

API最佳场景注意事项
requestAnimationFrame动画、高频渲染无需手动控制帧率
setTimeout单次延迟任务避免用于动画(可能卡顿)
setInterval低频轮询(如每 5 秒检查数据)注意清理(clearInterval

黄金法则

凡是涉及 视觉更新 的,优先用 requestAnimationFrame
非视觉任务(如逻辑控制),再用 setTimeout/setInterval


手写 setTimeoutsetInterval(JavaScript 实现)

由于 setTimeoutsetInterval 是浏览器/Node.js 提供的 Web API,我们无法完全用纯 JavaScript 实现它们(因为它们依赖底层事件循环机制)。但我们可以用 JavaScript 模拟 它们的行为,并理解其核心逻辑。


1. 手写 setTimeout(模拟版)

思路

• 使用 Date.now() 计算时间差。
• 用 requestAnimationFrame(浏览器)或 while 循环(Node.js)检查是否到达延迟时间。

代码实现(浏览器环境)

function mySetTimeout(callback, delay) {const startTime = Date.now();function checkTime() {const currentTime = Date.now();if (currentTime - startTime >= delay) {callback(); // 时间到了,执行回调} else {requestAnimationFrame(checkTime); // 继续检查}}requestAnimationFrame(checkTime);
}// 测试
mySetTimeout(() => console.log("Hello after 1s"), 1000);

说明
requestAnimationFrame 是浏览器 API,用于在下一帧渲染前执行回调(约 60fps)。
• 此方法 不精确requestAnimationFrame 不是严格计时器),但能模拟 setTimeout 的异步行为。


2. 手写 setInterval(模拟版)

思路

• 递归调用 mySetTimeout 实现循环执行。
• 用 clear 方法模拟 clearInterval

代码实现

function mySetInterval(callback, interval) {let timerId = null;function execute() {callback();timerId = mySetTimeout(execute, interval); // 递归调用}timerId = mySetTimeout(execute, interval);return {clear: () => {// 模拟 clearIntervalif (timerId) {// 这里需要实现 clearMyTimeout,但简化版无法真正取消console.log("Interval cleared");timerId = null;}}};
}// 测试
const interval = mySetInterval(() => console.log("Tick"), 1000);
setTimeout(() => interval.clear(), 5000); // 5秒后停止

问题
• 由于 mySetTimeout 无法真正取消(没有 clearMyTimeout),此方法 无法完全模拟 setInterval


3. 更精确的实现(基于 Promise + async/await

思路

• 用 Promise + setTimeout 模拟可控的 mySetTimeout
• 用 async/await 实现 mySetInterval

代码

// 精确版 mySetTimeout
function mySetTimeout(callback, delay) {return new Promise((resolve) => {setTimeout(() => {callback();resolve();}, delay);});
}// 精确版 mySetInterval
async function mySetInterval(callback, interval) {while (true) {await mySetTimeout(callback, interval);}
}// 测试
(async () => {mySetInterval(() => console.log("Tick"), 1000);
})();

特点
• 基于原生 setTimeout计时更精确
• 用 while(true) 实现循环,但 无法直接取消(需额外逻辑)。


4. 终极方案(完整模拟 clearTimeoutclearInterval

思路

• 用 Map 存储所有定时器 ID。
• 提供 clearMyTimeoutclearMyInterval 方法。

完整代码

const timers = new Map();
let id = 0;// 模拟 setTimeout
function mySetTimeout(callback, delay) {const timerId = id++;const startTime = Date.now();function checkTime() {const currentTime = Date.now();if (currentTime - startTime >= delay) {callback();timers.delete(timerId); // 执行后移除} else if (timers.has(timerId)) {requestAnimationFrame(checkTime); // 继续检查}}timers.set(timerId, true);requestAnimationFrame(checkTime);return timerId;
}// 模拟 clearTimeout
function clearMyTimeout(timerId) {if (timers.has(timerId)) {timers.delete(timerId); // 标记为取消}
}// 模拟 setInterval
function mySetInterval(callback, interval) {const timerId = id++;function execute() {if (!timers.has(timerId)) return; // 已取消callback();mySetTimeout(execute, interval); // 递归调用}timers.set(timerId, true);mySetTimeout(execute, interval);return timerId;
}// 模拟 clearInterval
function clearMyInterval(timerId) {clearMyTimeout(timerId); // 复用逻辑
}// 测试
const timeoutId = mySetTimeout(() => console.log("Timeout"), 1000);
const intervalId = mySetInterval(() => console.log("Interval"), 1000);setTimeout(() => {clearMyTimeout(timeoutId);clearMyInterval(intervalId);
}, 3000);

说明
• 用 Map 存储定时器 ID,clearMyTimeoutclearMyInterval 可以取消任务。
• 仍然依赖 requestAnimationFrame不是严格精确,但能模拟基本行为。


5. 总结

方法优点缺点
mySetTimeout简单模拟异步延迟不精确,依赖 requestAnimationFrame
mySetInterval模拟循环执行无法真正取消
Promise更接近原生行为仍依赖原生 setTimeout
终极方案支持取消,更完整代码较复杂

关键点

  1. setTimeoutsetInterval 是浏览器/Node.js 提供的 API,无法完全用 JS 实现。
  2. 模拟版依赖 requestAnimationFramePromise,无法做到完全精确。
  3. 最佳实践:直接使用原生 setTimeoutsetInterval,除非有特殊需求(如教学、自定义调度)。

希望这份指南帮你理解定时器的底层逻辑! 🚀

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com