Canvas动画

引题

最近忙完实验,在慢慢开始的学习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( 00, 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动画_第1张图片

实例二 散落的粒子

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

无数粒子是用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的,太多了,懒得去复制后面的。):

Canvas动画_第2张图片

你可能感兴趣的:(canvas)