zoukankan      html  css  js  c++  java
  • 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画的圆,不过半径我写成了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的,太多了,懒得去复制后面的。):

    散落的粒子

  • 相关阅读:
    安卓开发之有序广播
    安卓开发之无序广播
    安卓开发之短信发送器的开发
    安卓开发之隐式意图与显示意图
    安卓root权限 被提示Read-only file system 或 Operation not permitted 解决方案
    gcc 的基本使用和静态库、动态库的制作与使用
    Python类的学习,看这一篇文章就够了!
    Qt 添加程序图标和系统托盘图标
    Qt ListWidget item 发起拖放
    Qt 接受拖放
  • 原文地址:https://www.cnblogs.com/freestyle21/p/4457442.html
Copyright © 2011-2022 走看看