requestAnimationFrame实现canvas动画

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))
复制代码

转载于:https://juejin.im/post/5c3ef5505188252552516304

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值