requestAnimationFrame
-
window.requestAnimationFrame() 方法跟 setTimeout 类似,都是推迟某个函数的执行。不同之处在于,setTimeout 必须指定推迟的时间;
-
window.requestAnimationFrame() 则是推迟到浏览器下一次重流时执行,执行完才会进行下一次重绘。重绘通常是 16.7ms (60fps/s) 执行一次,不过浏览器会自动调节这个速率,比如网页切换到后台 Tab 页时,window.requestAnimationFrame() 会暂停执行。
-
如果某个函数会改变网页的布局,一般就放在 window.requestAnimationFrame() 里面执行,这样可以节省系统资源,使得网页效果更加平滑。因为慢速设备会用较慢的速率重流和重绘,而速度更快的设备会有更快的速率。
-
该方法接受一个回调函数作为参数。
window.requestAnimationFrame(callback)复制代码
-
window.requestAnimationFrame() 的返回值是一个整数,这个整数可以传入
-
window.cancelAnimationFrame(),用来取消回调函数的执行。
定时器 setInterval 实现 canvas 动画
代码实现:
复制代码
* { margin: 0; padding: 0;}canvas { border: 1px solid #ccc; margin: 20px;}button { width: 30px; height: 25px; margin: 5px;}复制代码
let cas = document.querySelector("#cas")let ctx = cas.getContext('2d')// 控制裁剪图片的 Y 坐标间接控制方向let dirIndex = 0let timer = nulllet img = new Image()img.src = "https://user-gold-cdn.xitu.io/2019/1/16/16855fe9b908c9af?w=160&h=260&f=png&s=5138" img.onload = function() { // 用来控制裁剪图片的 X 坐标 let xIndex = 0 clearInterval(timer) timer = setInterval(() => { // 清除之前的画布 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) ctx.drawImage( img, // 160 * 260 xIndex * 40, // 剪切图片的 x 坐标 dirIndex * 65, 40, // 剪切图像的宽度 65, 240, // 显示在画布位置的 x 坐标 150, 40, // 要使用的图像的宽度 65 // 高度 ) xIndex ++ xIndex %= 4 }, 200)}// 改变方向 通过图像的 y 坐标/* =====绑定点击事件======== */document.querySelector("#btn-dir-left").onclick = function(){ dirIndex = 1;}document.querySelector("#btn-dir-right").onclick = function(){ dirIndex = 2;}document.querySelector("#btn-dir-up").onclick = function(){ dirIndex = 3;}document.querySelector("#btn-dir-down").onclick = function(){ dirIndex = 0;}复制代码
requestAnimationFrame 实现 canvas 动画
上面主要 js 改进:
// 用来控制裁剪图片的 X 坐标let xIndex = 0step = (timestamp, elapsed) => { // 清除之前的画布 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) ctx.drawImage( img, // 160 * 260 xIndex * 40, // 剪切图片的 x 坐标 dirIndex * 65, 40, // 剪切图像的宽度 65, 240, // 显示在画布位置的 x 坐标 150, 40, // 要使用的图像的宽度 65 // 高度 ) xIndex ++ xIndex %= 4 requestAnimationFrame(step)}requestAnimationFrame(step)复制代码
上面说到,requestAnimationFrame 是推迟到浏览器下一次重流时执行,执行完才会进行下一次重绘。重绘通常是 16.7ms (60fps/s),那我们要是想让动画每秒 10 帧,该如何调节帧频呢?
上面也提到,requestAnimationFrame 返回值是一个整数;而 requestAnimationFrame 调用 callback 的时候会传入一个时间戳参数,可以根据这个参数来进行判断从而处理你实际需要的帧速;
比如要每秒/10 帧的话,可这样写:
let step = (timestamp, elapsed) => { if (elapsed > 1000 / 7) { //TO DO SOMETHING elapsed = 0 } requestAnimationFrame( _timestamp => step(_timestamp, elapsed + _timestamp - timestamp) )}requestAnimationFrame(timestamp => step(timestamp, 0))复制代码
上面代码可改写:
// 用来控制裁剪图片的 X 坐标let xIndex = 0step = (timestamp, elapsed) => { if (elapsed > 1000 / 10) { // 清除之前的画布 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) ctx.drawImage( img, // 160 * 260 xIndex * 40, // 剪切图片的 x 坐标 dirIndex * 65, 40, // 剪切图像的宽度 65, 240, // 显示在画布位置的 x 坐标 150, 40, // 要使用的图像的宽度 65 // 高度 ) xIndex ++ xIndex %= 4 elapsed = 0 } requestAnimationFrame( _timestamp => step(_timestamp, elapsed + _timestamp - timestamp) )}requestAnimationFrame(timestamp => step(timestamp, 0))复制代码