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 动画
代码实现:
<canvas id="cas" width="600px" height="500px">您的浏览器不支持canvas,请升级您的浏览器</canvas>
<button id="btn-dir-left">←</button>
<button id="btn-dir-right">→</button>
<button id="btn-dir-up">↑</button>
<button id="btn-dir-down">↓</button>
复制代码
* {
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 = 0
let timer = null
let img = new Image()
img.src = "https://user-gold-cdn.xitu.io/2019/1/16/16855fe9b908c9af?w=160&h=260&f=png&s=5138"
<!-- setInterval -->
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 = 0
step = (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 = 0
step = (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))
复制代码