• Canvas动画


    Canvas动画


    在 canvas 上绘制内容是用 canvas 提供的或者自定义的方法,而通常我们仅仅在脚本执行结束后才能看见结果,所以想在 for 循环里面完成动画是不可能的。那么为了实现动画,我们需要一些可以定时执行重绘的方法。

    setInterval(function, delay)
    定时器,当设定好间隔时间后,function 会定期执行。

    setTimeout(function, delay)
    延时器,在设定好的时间之后执行函数

    requestAnimationFrame(callback)
    告诉浏览器你希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画。

    如果不需要与用户互动,可以使用 setInterval() 方法,它可以定期执行指定的代码。
    如果需要做游戏,可以使用键盘或者鼠标事件配合上 setTimeout() 方法来实现。
    通过设置事件监听,可以捕捉用户的交互,并执行相应的动作。

    requestAnimationFrame()方法提供了更加平缓且有效率的方式来执行动画,当系统准备好重绘条件后才会调用绘制动画帧。一般每秒钟回调函数执行 60 次,也有可能会被降低

    因为通常情况下requestAnimationFrame()方法会遵循 W3C 的建议,浏览器中的回调函数执行次数通常与浏览器屏幕刷新次数相匹配。
    还有为了提高性能和电池寿命,通常 requestAnimationFrame() 方法运行在后台标签页或者隐藏时,requestAnimationFrame() 方法会暂停调用以提升性能和电池寿命。

    总结一下绘制动画的基本步骤

    • 清空 canvas:除非接下来要画的内容会完全充满 canvas(例如背景图),否则需要清空所有。最简单的做法就是用 clearRect 方法。
    • 保存 canvas 状态:如果要改变 canvas 状态的设置(样式,变形之类的),之后又要在每画一帧之时都是原始状态的情况时,需要先保存一下。
    • 绘制动画图形(animated shapes)
    • 恢复 canvas 状态:如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。



    const canvas = document.querySelector('.canvas')
        const sun = new Image()
        const moon = new Image()
        const earth = new Image()
        let time = +new Date()
        if (canvas.getContext) {
            const ctx = canvas.getContext('2d')
            ctx.globalCompositeOperation = 'destination-over'
            const init  = () => {
                sun.src = './doc/imgLib/else/sun.png'
                moon.src = './doc/imgLib/else/moon.png'
                earth.src = './doc/imgLib/else/earth.png'
                window.requestAnimationFrame(draw)
            }
            const draw = () => {
                ctx.clearRect(0, 0, 500, 500)
                ctx.fillStyle = "rgba(3, 3, 3, 0.1)"
                ctx.strokeStyle = "rgba(0, 0, 0, 0.1)"
                ctx.save()
                ctx.save()
                ctx.save()
                const nowTime = +new Date()
                const decSec = nowTime - time
                
                ctx.beginPath()
                ctx.translate(250, 250)
                ctx.rotate(Math.PI * 2 / 6000 * decSec)
                ctx.translate(200, 0)
                ctx.drawImage(earth, 20 , 20, 300, 300, -30, -30, 60, 60)
                // ctx.restore()
    
    
                ctx.beginPath()
                // ctx.translate(250, 250)
                ctx.rotate(Math.PI * 2 / 2000 * decSec)
                ctx.translate(60, 0)
                ctx.drawImage(moon, 20 , 20, 300, 300, -20, -20, 40, 40)
                ctx.restore()
    
    
    
                ctx.translate(250, 250)
                ctx.beginPath()
                ctx.arc(0, 0, 200, 0, Math.PI * 2, false);
                ctx.drawImage(sun, 20 , 20, 300, 300, -40, -40, 80, 80)
                ctx.stroke()
                ctx.restore()
    
                window.requestAnimationFrame(draw)
                
            }
            init()
            
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54



    高级动画

    在初级动画的基础上加上一些符合物理的运动,这样就能使动画更生动

    const canvas = document.querySelector('.canvas')
        if (canvas.getContext) {
            const ctx = canvas.getContext('2d')
            const init  = () => {
                window.requestAnimationFrame(draw)
            }
            const ball = {
                x: 50,
                y: 50,
                vx: 1,
                vy: 3,
                radius: 20,
                speed: .25,
                color: "rgba(3, 3, 3, 0.1)",
                draw: function() {
                    ctx.beginPath()
                    ctx.fillStyle = this.color
                    ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false)
                    ctx.closePath()
                    ctx.fill()
                }
            }
            const draw = () => {
                // ctx.clearRect(0, 0, 500, 500)
                // 用带透明度的矩形代替清空就可以保留上一次运动的痕迹
                ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
                ctx.fillRect(0, 0, canvas.width, canvas.height)
                ctx.save()
    
                ball.draw()
    
                ball.y += ball.vy
                ball.x += ball.vx
    
                ball.vy = ball.vy * .99
                ball.vx = ball.vx * .9995
                
                ball.vy += ball.speed
                ball.speed = ball.speed - .0002 > 0 ? ball.speed - .0002: 0
                if(ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0){
                    ball.vy = -ball.vy
                }
                if(ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0){
                    ball.vx = -ball.vx
                }
    
                ctx.restore()
                window.requestAnimationFrame(draw)        
            }
            init()
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
  • 相关阅读:
    项目的一些难点
    Java装饰者模式详解:为对象动态添加功能
    时间日期类
    1326. 灌溉花园的最少水龙头数目 动态规划
    python tkinter 使用
    [移动通讯]【Carrier Aggregation-4】【LTE-5】
    高精度算法详解
    微信快捷回复软件
    学习笔记0-认证
    Andriod Studio小游戏
  • 原文地址:https://blog.csdn.net/Raccon_/article/details/127549317