zoukankan      html  css  js  c++  java
  • ball animate

    经过前面的文章,我们已经能够在canvas画布上画出各种炫酷的图形和画面,但是这些画面都是禁止的,怎么样才能让他们动起来呢?

    动画的基本步骤

    我们知道,动画是一帧一帧的画面不断反映实现的,人的眼睛看到一幅画或一个物体后,在0.34秒内不会消失。利用这一原理,在一幅画还没有消失前播放下一幅画,就会给人造成一种流畅的视觉变化效果。在canvas中,就是在绘制完当前画面之后,快速的绘制下一个画面。步骤如下:

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

    操纵动画

    在 canvas 上绘制内容是用 canvas 提供的或者自定义的方法,而通常,我们仅仅在脚本执行结束后才能看见结果,比如说,在 for 循环里面做完成动画是不太可能的。

    因此,为了实现动画,我们需要一些可以定时执行重绘的方法。window对象提供了下面的方法实现定时动画:

    • setInterval(function, delay)当设定好间隔时间后,function会定期执行
    • setTimeout(function, delay)在设定好的时间之后执行函数
    • requestAnimationFrame(callback)告诉浏览器你希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画。

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

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

    在使用window.requestAnimationFrame()方法的过程中,我推荐使用下面的兼容性方法来代替:

    window.requestAnimationFrame = (function(){
        return  window.requestAnimationFrame       ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame    ||
                window.oRequestAnimationFrame      ||
                window.msRequestAnimationFrame     ||
                function (callback) {
                    window.setTimeout(callback, 1000 / 60);
                };
    })();
    

    canvas动画实例-模拟小球自由落体运动

    上面介绍了canvas动画的基本概念,接下来我们将会在canvas中实现小球下落的动画。小球的完整代码再本文结尾。点击可跳转到结尾

    绘制小球

    首先需要在canvas上绘制一个小球。

    var ctx = document.getElementById('canvas').getContext('2d');
    if (!ctx) {
        console.log('您的浏览器不支持canvas');
        // 可以抛出异常强制结束JS执行
        throw new Error("Do not support canvas");
    }
    
    var ball = {
        x: 100,  // 小球的x坐标
        y: 100,  // 小球的y坐标
        radius: 25,  // 小球半径
        color: 'cyan', // 小球颜色
        draw: function() {  // 绘制小球的函数
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
            ctx.closePath();
            ctx.fillStyle = this.color;
            ctx.fill();
        },
        clear: function() {  // 清除小球区域的函数
            ctx.clearRect(this.x - this.radius,
                    this.y - this.radius,
                    this.radius * 2,
                    this.radius * 2);
        }
    }
    ball.draw(); // 绘制小球
    

    添加运动描述

    绘制了小球之后,要添加动画,还需要为小球添加速率矢量进行移动。另外,速度也是变化的量,对于只有落体运动,还有竖直方向的重力加速度,所以还需要为小球加上加速度。

    var ball = {
            x: 100,  // 小球的x坐标
            y: 100,  // 小球的y坐标
            vx: 0,   // 小球水平方向速度
            vy: 0,   // 小球竖直方向速度
            ax: 0,   // 小球水平方向加速度
            ay: 0,   // 小球竖直方向加速度
            dt: 1,   // 两帧之间的时间为1个单位时间
            radius: 25,  // 小球半径
            color: 'cyan', // 小球颜色
            s: function(v, a, t) {
                // 匀加速直线运动的位移公式:s=vt+1/2at^2
                return v * t + (1 / 2.0) * a * t * t;
            },
            dx: function() {
                // 计算水平方向的位移
                return this.s(this.vx, this.ax, this.dt);
            },
            dy: function() {
                // 计算竖直方向的位置
                return this.s(this.vy, this.ay, this.dt);
            },
            next: function() {
                // 计算小球下一时刻的位移
                this.x += this.dx();
                this.y += this.dy();
    
                // 计算小球下一时刻的速度:v_t = v_0 + a*t
                this.vx = this.vx + this.ax * this.dt;
                this.vy = this.vy + this.ay * this.dt;
    
                this.boundary(0, canvas.width, canvas.height, 0);
            },
        };
    
    

    假设每一帧之间的时间是单位时间,那么根据当前小球的位置速度和加速度,我们就可以计算下一帧的小球的位置和速度,此时清空上一帧的canvas,再绘制下一帧,即可实现动画效果。

    var animate; // 记录动画
    ball.draw();
    
    // 绘制一帧
    function draw() {
        // 1:清空画布
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        // 2:绘制小球
        ball.draw();
        // 3:计算小球的下一个状态
        ball.next();
        // 4:进入下一帧
        animate = window.requestAnimationFrame(draw);
    }
    

    边界处理

    若没有任何的碰撞检测,我们的小球很快就会超出画布。我们需要检查小球的 x 和 y 位置是否已经超出画布的尺寸以及是否需要将速度矢量反转。

    boundary: function(top, right, bottom, left) {
        // 检测小球下一帧是否出界,出界则补正
        if (this.y > bottom) {  // 下边界越界
            this.vy = -this.vy;  // 速度反向
        } else if (this.y < top) {
            this.vy = -this.vy;
        } else if (ball.x > right) {
            this.vx = -this.vx;  // 速度反向
        } else if (ball.x < left) {
            this.vx = -this.vx;
        }
    
    }
    

    添加拖尾效果

    为了使得小球运动更加逼真,可以添加拖尾效果。使用clearRect函数清除前一帧动画时,若用一个半透明的fillRect函数取代之,就可轻松制作长尾效果。

    ctx.fillStyle = 'rgba(255,255,255,0.3)';
    ctx.fillRect(0,0,canvas.width,canvas.height);
    

    移动鼠标到canvas内可让小球动起来!

    遗留问题和优化

    在实际的生活中,小球碰撞到地面反弹的时候,反弹的高度会越来越低,因为碰撞地面损失了一部分速度。

    boundary: function(top, right, bottom, left) {
        // 检测小球下一帧是否出界,出界则补正
        if (this.y > bottom) {  // 下边界越界
            this.vy = -this.vy;  // 速度反向
            this.vy = 0.9 * this.vy; // 速度损失
        } else if (this.y < top) {
            this.vy = -this.vy;
        } else if (ball.x > right) {
            this.vx = -this.vx;  // 速度反向
        } else if (ball.x < left) {
            this.vx = -this.vx;
        }
    }
    

    上面这种方式会偶尔使得小球无法反弹。

    在碰撞地面的时候,小球的反弹之后的速度和位移,准确值需要根据严格的匀加速公式以及损失之后的速度来计算。

    边界检查时上述方法是检查圆心和边界的位置,更好的方式是检查圆周和边界的距离。

    源码可以以及效果可以参考这儿:本文实例

    上述所有方式的源代码如下:

    ```html ball animate ```
  • 相关阅读:
    【ORA-02049】超时分布式事务处理等待锁 解决方法
    Git使用出错:Couldn‘t reserve space for cygwin‘s heap, Win32
    JS身份证号码校验
    linux 下查看cpu位数 内核等参数命令(转)
    linux ps命令,查看进程cpu和内存占用率排序(转)
    JAVA图片验证码
    JAVA BigDecimal 小数点处理
    Linux命令大全
    Eclipse Java注释模板设置详解
    JSONArray的应用
  • 原文地址:https://www.cnblogs.com/youyoui/p/8525730.html
Copyright © 2011-2022 走看看