经过前面的文章,我们已经能够在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()方法的过程中,我推荐使用下面的兼容性方法来代替:

  1. window.requestAnimationFrame = (function(){
  2. return window.requestAnimationFrame ||
  3. window.webkitRequestAnimationFrame ||
  4. window.mozRequestAnimationFrame ||
  5. window.oRequestAnimationFrame ||
  6. window.msRequestAnimationFrame ||
  7. function (callback) {
  8. window.setTimeout(callback, 1000 / 60);
  9. };
  10. })();

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

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

绘制小球

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

  1. var ctx = document.getElementById('canvas').getContext('2d');
  2. if (!ctx) {
  3. console.log('您的浏览器不支持canvas');
  4. // 可以抛出异常强制结束JS执行
  5. throw new Error("Do not support canvas");
  6. }
  7. var ball = {
  8. x: 100, // 小球的x坐标
  9. y: 100, // 小球的y坐标
  10. radius: 25, // 小球半径
  11. color: 'cyan', // 小球颜色
  12. draw: function() { // 绘制小球的函数
  13. ctx.beginPath();
  14. ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
  15. ctx.closePath();
  16. ctx.fillStyle = this.color;
  17. ctx.fill();
  18. },
  19. clear: function() { // 清除小球区域的函数
  20. ctx.clearRect(this.x - this.radius,
  21. this.y - this.radius,
  22. this.radius * 2,
  23. this.radius * 2);
  24. }
  25. }
  26. ball.draw(); // 绘制小球

添加运动描述

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

  1. var ball = {
  2. x: 100, // 小球的x坐标
  3. y: 100, // 小球的y坐标
  4. vx: 0, // 小球水平方向速度
  5. vy: 0, // 小球竖直方向速度
  6. ax: 0, // 小球水平方向加速度
  7. ay: 0, // 小球竖直方向加速度
  8. dt: 1, // 两帧之间的时间为1个单位时间
  9. radius: 25, // 小球半径
  10. color: 'cyan', // 小球颜色
  11. s: function(v, a, t) {
  12. // 匀加速直线运动的位移公式:s=vt+1/2at^2
  13. return v * t + (1 / 2.0) * a * t * t;
  14. },
  15. dx: function() {
  16. // 计算水平方向的位移
  17. return this.s(this.vx, this.ax, this.dt);
  18. },
  19. dy: function() {
  20. // 计算竖直方向的位置
  21. return this.s(this.vy, this.ay, this.dt);
  22. },
  23. next: function() {
  24. // 计算小球下一时刻的位移
  25. this.x += this.dx();
  26. this.y += this.dy();
  27. // 计算小球下一时刻的速度:v_t = v_0 + a*t
  28. this.vx = this.vx + this.ax * this.dt;
  29. this.vy = this.vy + this.ay * this.dt;
  30. this.boundary(0, canvas.width, canvas.height, 0);
  31. },
  32. };

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

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

边界处理

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

  1. boundary: function(top, right, bottom, left) {
  2. // 检测小球下一帧是否出界,出界则补正
  3. if (this.y > bottom) { // 下边界越界
  4. this.vy = -this.vy; // 速度反向
  5. } else if (this.y < top) {
  6. this.vy = -this.vy;
  7. } else if (ball.x > right) {
  8. this.vx = -this.vx; // 速度反向
  9. } else if (ball.x < left) {
  10. this.vx = -this.vx;
  11. }
  12. }

添加拖尾效果

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

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

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

遗留问题和优化

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

  1. boundary: function(top, right, bottom, left) {
  2. // 检测小球下一帧是否出界,出界则补正
  3. if (this.y > bottom) { // 下边界越界
  4. this.vy = -this.vy; // 速度反向
  5. this.vy = 0.9 * this.vy; // 速度损失
  6. } else if (this.y < top) {
  7. this.vy = -this.vy;
  8. } else if (ball.x > right) {
  9. this.vx = -this.vx; // 速度反向
  10. } else if (ball.x < left) {
  11. this.vx = -this.vx;
  12. }
  13. }

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

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

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

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

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

```html

ball animate

body {
margin:0;
padding:0;
height: 100%;
/*background: #000;*/
overflow: hidden;
}
canvas {
padding: 0;
background: #000;
border: 1px solid;
}

```

canvas动画:自由落体运动的更多相关文章

  1. canvas 时钟+自由落体

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  2. 卡尔曼滤波器实例:跟踪自由落体运动:设计与Matlab实现

    [首发:cnblogs    作者:byeyear    Email:byeyear@hotmail.com] 本文所用实例来自于以下书籍: Fundamentals of Kalman Filter ...

  3. OpenGL绘制自由落体小球

    OpenGL绘制自由落体小球 一.    程序运行的软硬件环境 本次设计在window10系统下进行,运用C++进行编写,在CodeBlocks环境下使用OpenGL进行设计. 所需环境配置分为2部分 ...

  4. js实现自由落体

    实现自由落体运动需要理解的几个简单属性: clientHeight:浏览器客户端整体高度 offsetHeight:对象(比如div)的高度 offsetTop:对象离客户端最顶端的距离 <!d ...

  5. Javascript摸拟自由落体与上抛运动 说明!

    JavaScript 代码 //**************************************** //名称:Javascript摸拟自由落体与上抛运动! //作者:Gloot //邮箱 ...

  6. 使用CSS3动画模拟实现小球自由落体效果

    使用纯CSS代码模拟实现小球自由落体效果: html代码如下: <div id="ballDiv"> <div id="ball">&l ...

  7. 【30分钟学完】canvas动画|游戏基础(5):重力加速度与模拟摩擦力

    前言 解决运动和碰撞问题后,我们为了让运动环境更加自然,需要加入一些环境因子,比如常见的重力加速度和模拟摩擦力. 阅读本篇前请先打好前面的基础. 本人能力有限,欢迎牛人共同讨论,批评指正. 重力加速度 ...

  8. HTML5 Canvas彩色小球碰撞运动特效

    脚本简介 HTML5 Canvas彩色小球碰撞运动特效是一款基于canvas加面向对象制作的运动小球动画特效.   效果展示 http://hovertree.com/texiao/html5/39/ ...

  9. Android游戏开发:物理游戏之重力系统开发--圆形自由落体Demo

    本节为大家提供有关物理游戏的知识,讲解了一个简单的圆形自由落体Demo的编写.. Java代码 package com.himi; import java.util.Random; import ja ...

随机推荐

  1. “字符串替换” 和 “模板设置” (application/config.php)

    //视图输出字符串内容替换'view_replace_str' => [ '__PUBLIC__' => '/public/', '__ROOT__' => '/',], 模板设置: ...

  2. 制作 alipay-sdk-java包到本地仓库

    一.首先 搭建好maven 基础环境,本地可以运行maven 命令 从支付宝官网上下载sdk https://doc.open.alipay.com/doc2/detail?treeId=54& ...

  3. 洛谷 [P1024]一元三次方程求解

    一道水题然而坑点很多. #include <iostream> #include <cstdio> #include <algorithm> #include &l ...

  4. 洛谷 [P2661] 信息传递

    求有向图的权值为一的最小环 并查集做法 维护一个dis[],表示i号元素到fa[i]的距离. 对于输入的每两个点u,v,询问这两个点的fa[]是否相同,如果相同就成环,维护最小值,mi=min(mi, ...

  5. 剑指offer试题(PHP篇一)

    1.二维数组中的查找 题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. ...

  6. win7(windows 7)系统下安装SQL2005(SQL Server 2005)图文教程

    操作系统:Microsoft Windows 7 旗舰版(32位) 数据库版本:SQL Server 2005 简体中文开发板 数据库下载链接: https://pan.baidu.com/s/1cq ...

  7. ansible实践-1

      不需要安装客户端,通过sshd去通信 基于模块工作,模块可以由任何语言开发 不仅支持命令行使用模块,也支持编写yaml格式的playbook 支持sudo 有提供UI(浏览器图形化)www.ans ...

  8. href与src 区别

    src 是可替换的文本支撑,将指向的内容引入文档当前标签所在的位置, 当浏览器解析到该标签时,将暂停其它资源的下载处理, 请求该标签的src ,下载指向的外部资源并应用到当前文档, 所以js 脚本一般 ...

  9. Selenium常用API用法示例集----下拉框、文本域及富文本框、弹窗、JS、frame、文件上传和下载

    元素识别方法.一组元素定位.鼠标操作.多窗口处理.下拉框.文本域及富文本框.弹窗.JS.frame.文件上传和下载 元素识别方法: driver.find_element_by_id() driver ...

  10. Java经典编程题50道之四十二

    809*??=800*??+9*??+1,其中??代表的两位数,8*??的结果为两位数,9*??的结果为3位数.求??代表的两位数,以及809*??后的结果. public class Example ...