博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Canvas动画
阅读量:6434 次
发布时间:2019-06-23

本文共 4765 字,大约阅读时间需要 15 分钟。

引题

最近忙完实验,在慢慢开始的学习HTML5 的canvas,很早就听过动态canvas的威名,一直没有机会和时间学习。这次系统学习HTML5和css3,自然不会放过这次机会。

本文主要是总结学习canvas动画的一些基本知识,并不会介绍canvas,关于入门读者朋友可以回去翻翻书本。另外本文还会试着写几个实例,加深理解。

canvas动画基本思想

思想一 刷新重绘

我们平时看到的canvas游戏 里,人物运动、物体变化都是基于这个思想。比如绘制动画的圆,我们通过鼠标触动或者重力等外界因素改变圆的圆心坐标。下一步,通过:

context.clearRect(0, 0, canvas.width, canvas.height)
把当前canvas的画面清空。然后,再通过:
context.art(circle.x, circle.y, circle.radius, 0, Math.PI*2)
 

利用新的圆心坐标绘制新的圆。

刷新重绘的思想是canvas游戏的核心,任何需要移动的物体都是通过这种方式。

不过在即时战略游戏中,很多东西不需要更新,需要的只是人物,这样我们全部清楚再重绘就有点得不偿失。这里就需要用到一种区域适配更新的技术,是John Carmack发明的,叫做ATR技术。有兴趣的朋友可以去google一下。

思想二 碰撞检测

碰撞检测用的比较比较频繁。比如和canvas上的圆交互的时候,需要检测点击的是哪个圆,只需循环判断触点坐标到圆心的距离与半径的关系即可。再比如迷宫游戏里面,移动人物的时候需要判断前方是否是墙壁,是墙壁的话就不能移动。这个碰撞检测的方法不太常见,是通过像素检测的方式。如果前方的像素是我们预定的墙壁的颜色,就不能移动了。

一些需要知道的东西

动画对于重绘速度有要求,通常每秒30次以上才不会导致出现间断的现象。由于绘制速度很快,可能你会觉得我们的画布会反应迟钝。但是,由于很多现代浏览器都使用了硬件加速等性能增强技术,把图像处理工作转移给显卡,从而节省了CPU。由此引申出一个css hack技术。开启硬件加速,css如下:

transform: translateZ(0); transform: translate3d(0, 0, 0);
圆圈绘图程序每次刷新画布,都会先使用clearRect清除所有,再绘制所有(比上次多). 这样不会出现闪烁,因为Canvas针对这个问题进行了优化,它实际上会在绘图逻辑执行完成后才清除或绘制所有内容。

canvas动画实例

实例一 自由落体交互的小球

要不断的移动小球,正如上面所说,就需要不断重绘canvas

setTimeOut(“drawFrame()”, 20) 接下来看看drawFrame函数:

  
  function drawFrame() {
        
//
清空
        context.clearRect(
0
0, canvas.width, canvas.height);
        context.beginPath();
        
//
balls是一个存放我们小球的数组。
        
for(
var i=
0; i<balls.length; i++) {
          
var ball = balls[i];
          ball.x += ball.dx;
          ball.y += ball.dy;
          
//
 添加重力效果
          
if ((ball.y) < canvas.height) ball.dy += 
0.22;
          
//
 添加摩擦力效果
          ball.dx = ball.dx * 
0.998;
          
//
 碰到墙反弹
          
if ((ball.x + ball.radius > canvas.width) || (ball.x - ball.radius < 
0)) {
            ball.dx = -ball.dx;
          }
          
//
 碰到地反弹注意0.96哦!
          
if ((ball.y + ball.radius > canvas.height) || (ball.y - ball.radius < 
0)) { 
            ball.dy = -ball.dy0.
96
          }
          context.fillStyle = 
"
white
";
          
//
 Draw 开始绘制球
          context.arc(ball.x, ball.y, ball.radius, 
0, Math.PI2);
          context.lineWidth = 
1;
          context.fill();
          context.stroke(); 
        }
        setTimeout(
"
drawFrame()
"
20);}

      

drawFrame函数是动画的核心,处理外力对小球的影响。考虑了重力摩擦力等。然后不断调整小球的坐标,刷新重绘。

下面是我们处理鼠标交互的函数:

  
  function canvasClick(e) {
        
//
 触点位置
        
var clickX = e.pageX - canvas.offsetLeft;
        
var clickY = e.pageY - canvas.offsetTop;
        
//
 边界检测,看点击哪个球。
        
for(
var i 
in balls)
        {
          
var ball = balls[i];
          
if ((clickX > (ball.x-ball.radius)) && (clickX < (ball.x+ball.radius)))
          {
            
if ((clickY > (ball.y-ball.radius)) && (clickY < (ball.y+ball.radius)))
            {
              
//
 改变小球的速度。
              ball.dx -= 
2;
              ball.dy -= 
3;
              
return;
            }
          }
        }}

      

最后是增加小球和清空画布:

  
  function addBall() {
        
var radius = parseFloat(document.getElementById(
"
ballSize
").value);
        
var ball = 
new Ball(
50,
50,
1,
1,radius);
        balls.push(ball);
      }
      function clearBalls() {
        
//
 清空数组,下次重绘的就是空得了。
        balls = [];
     }

      

最终结果:

实例二 散落的粒子

这次,写一个无数粒子构成的名字,鼠标接触马上散落各处的例子。

无数粒子是用canvas画的圆,不过半径我写成了1px。这里就不展示了。

下面看核心函数,处理小球运动的轨迹:

  
    function updateBalls(canvas, balls, timeDiff, mousePos) {
         
var context = canvas.getContext(
'
2d
');
          
var collisionDamper = 
0.3;
//
碰撞减缓
          
var floorFriction = 
0.0005 * timeDiff;
//
摩擦力
          
var mouseForceMultiplier = 
1 * timeDiff;
//
Multiplier 乘数
          
var restoreForce = 
0.002 * timeDiff;
          
//
和上个例子一样,循环小球数组,改变每个小球的圆心坐标。
          
for(
var n = 
0; n < balls.length; n++) {
              
var ball = balls[n];
              
//
 改变圆心坐标
              ball.y += ball.vy;
              ball.x += ball.vx;
              
//
 恢复力,最后要静止。
              
if(ball.x > ball.origX) {
                    ball.vx -= restoreForce;
              } 
else {
                    ball.vx += restoreForce;
              }
              
if(ball.y > ball.origY) {
                    ball.vy -= restoreForce;
              } 
else {
                    ball.vy += restoreForce;
              }
              
//
 鼠标给的力
              
var mouseX = mousePos.x;
              
var mouseY = mousePos.y;
              
var distX = ball.x - mouseX;
//
判断左右的。
              
var distY = ball.y - mouseY;
              
var radius = Math.sqrt(Math.pow(distX, 
2) + Math.pow(distY, 
2));
              
var totalDist = Math.abs(distX) + Math.abs(distY);
              
var forceX = (Math.abs(distX) / totalDist) * (
1 / radius) *   mouseForceMultiplier;
              
var forceY = (Math.abs(distY) / totalDist) * (
1 / radius) * mouseForceMultiplier;
              
if(distX > 
0) {
                   
//
 如果从左边触动小球
                   ball.vx += forceX;
              } 
else {
                   ball.vx -= forceX;
              }
              
if(distY > 
0) {
                   
//
 从上方触动小球
                   ball.vy += forceY;
              } 
else {
                   ball.vy -= forceY;
              }
              
//
 考虑地板摩擦力
              
if(ball.vx > 
0) {
                    ball.vx -= floorFriction;
              } 
else 
if(ball.vx < 
0) {
                    ball.vx += floorFriction; 
              }
              
if(ball.vy > 
0) {
                    ball.vy -= floorFriction;
              } 
else 
if(ball.vy < 
0) {
                    ball.vy += floorFriction;
              }
              
//
 碰到地板反弹
              
if(ball.y > (canvas.height - ball.radius)) {
                     ball.y = canvas.height - ball.radius - 
2;
                     ball.vy *= -
1;
                     ball.vy *= (
1 - collisionDamper);
              }
              
//
 碰到上顶反弹
              
if(ball.y < (ball.radius)) {
                     ball.y = ball.radius + 
2;
                     ball.vy *= -
1;
                     ball.vy *= (
1 - collisionDamper);
              }
              
//
 碰到右边反弹
              
if(ball.x > (canvas.width - ball.radius)) {
                     ball.x = canvas.width - ball.radius - 
2;
                     ball.vx *= -
1;
                     ball.vx *= (
1 - collisionDamper);
             }
              
//
 碰到左边反弹
              
if(ball.x < (ball.radius)) {
                     ball.x = ball.radius + 
2;
                     ball.vx *= -
1;
                     ball.vx *= (
1 - collisionDamper);
              }
          }
    }

     

就像注释的一样,考虑了反弹、地板摩擦力、鼠标力、恢复力等。通过这个函数改变小球的位置。

剩下其他几个不太重要的函数就不写了,主要是循环小球数组重绘,寻找鼠标触点位置等,和第一个差不多。

效果(本来想写freestyle的,太多了,懒得去复制后面的。):

转载于:https://www.cnblogs.com/freestyle21/p/4457442.html

你可能感兴趣的文章
AI、新材料、5G、智慧城市,未来的社会场景在高交会提前上演
查看>>
Facebook开发的一种数据查询语言——GraphQL:安全概述和测试技巧
查看>>
ECS主动运维2.0,体验升级,事半功倍
查看>>
vim 学习方法
查看>>
php token验证范例
查看>>
WebSocket的C++服务器端实现
查看>>
java中两种添加监听器的策略
查看>>
脑洞成现实!AI系统可提前10s预测地震
查看>>
Page页面生命周期——微信小程序
查看>>
Node.js编写CLI的实践
查看>>
Javascript数组对象的方法和属性
查看>>
TiDB 3.0.0 Beta.1 Release Notes
查看>>
oracle数据库的启动和停止
查看>>
《LoadRunner没有告诉你的》之七——使用 LoadRunner 连续长时间执行测试,如何保证参数化的数据足够又不会重复?...
查看>>
python easy_install django 安装
查看>>
读《图解HTTP》总结--第六章
查看>>
毕业就能拿到上万薪资的程序员他们都做了啥?
查看>>
最小的k个数
查看>>
Spring cloud Eureka(注册服务中心)
查看>>
FWSM-no connection SYN ACK
查看>>