zoukankan      html  css  js  c++  java
  • 突袭HTML5之Canvas 2D入门5 事件与动画

    Canvas & SVG & DOM 

      Canvas与SVG都是2D绘图的利器,除此之外,使用CSS、DOM也可以实现某些性状的绘制,而且在动画中,也都可以使用这些技术实现动画效果。这里就简单比较一下这些技术。

    • canvas:canvas是以画像素的形式画出图形,它没有shape和vector的概念。所以就没有对象去接受事件,它只是去绘制像素点。这是缺点,也是优点。
    • SVG:SVG是基于vector来绘制shape。每个shape都可以接受事件。向量图的最大优势就是缩放不失真。这是canvas做不到的。
    • CSS:CSS是对DOM对象添加样式的。因为canvas中没有DOM对象,所以不能对canvas中的图形使用CSS。CSS只能作用于canvas自身,比如背景色、边框,但是也仅限于此。
    • DOM动画:DOM定义了屏幕上的一切对象。DOM动画,不管是使用CSS实现,还是使用Javascript实现,都可以做的比canvas平滑。但是这是与浏览器实现相关的。

      从上面看到,canvas的限制很多,那么我们为什么还要使用cavas呢?
      首先,canvas是更底层的技术,你能方便的控制绘制过程,内存使用量也比较低,但是代价就是需要写更多的代码。SVG的优势在于可以利用现有的shape去绘制。CSS或者DOM动画的优势在于动画大范围的区域。
      其次,如果你想去对图,图形,动态图表,视频游戏使用3D的变换时,canvas是比较好的选择。

      此外还有一点要注意,canvas对2D绘图提供了直接的API支持,但是3D的API是WebGL提供的。WebGL是更底层的技术,难于使用,但是也更加强大。

    事件
      事件是的基础,这里也简单介绍一下canvas的事件。canvas没有定义任何新的事件,你仍然可以像以前那样去监听任何的鼠标事件。上面也说到了,canvas的内部就是一系列的像素,它们不响应任何的事件,所以浏览器不知道canvas内部是什么东西。
      如果你想让内部的图形接受事件,对canvas来说几乎是不可能呢。但是幸运的是,你还是可以知道给定的点在不在当前的path中,例子如下:

    c.beginPath();
    c.arc(
        100,100, 40,  //40 pix radius circle at 100,100
        0,Math.PI*2,  //0 to 360 degrees for a full circle
    );
    c.closePath();
    var a = c.isPointInPath(80,0);     // returns true
    var b = c.isPointInPath(200,100);  // returns false

      检测点是否在一个图形(规则的图形,比如说自己画的按钮)中,通常的做法是比较该点的坐标与图形的各个顶点的坐标。

    基本动画
      动画的基本思路是用脚本去操控canvas对象,这样要实现一些交互动画也是相当容易的。只不过,canvas不是专门为动画而设计的(不像Flash),所以操作起来会有些限制。
      最大的限制就是图像一旦绘制出来,它就是一直保持那样了。如果需要移动它,我们不得不重绘所有的东西。重绘是相当费时的,而且性能依赖于电脑的速度。

    基本动画的步骤:
    1.画一帧,需要以下一些步骤:
    (1)清空 canvas
      除非接下来要画的内容会完全充满 canvas(例如背景图),否则你需要清空所有。最简单的做法就是用clearRect方法。
    (2)保存 canvas 状态
      如果你要改变一些会改变canvas状态的设置(样式,变形之类的),又要在画每一帧之时都是原始状态的话,你需要先保存一下。
    (3)绘制动画图形(animated shapes)
      这一步才是重绘动画帧,一般的步骤都是去生成新图形,更新已有的图形位置,清除已经移动到canvas外面的图形,最后绘制出新的场景。
    (4)恢复canvas状态
      如果已经保存了canvas的状态,可以先恢复它,然后重绘下一帧。
    2.按照一定的设置去计算图形的变换,重绘新的帧,画每一帧的过程是一样的。
    通常有两种方式去设置重绘的频率:
    (1)定时重绘
      第一种方式是通过setInterval和setTimeout方法来控制在设定的时间点上执行重绘。
    setInterval(animateShape,500); 
    setTimeout(animateShape,500); 
      setTimeout和setInterval的语法相同。它们都有两个参数,一个是将要执行的代码字符串,还有一个是以毫秒为单位的时间间隔,当过了那个时间段之后就将执行那段代码。
    不过这两个函数还是有区别的,setInterval在执行完一次代码之后,经过了那个固定的时间间隔,它还会自动重复执行代码,而setTimeout只执行一次那段代码。
    如果你不需要任何交互操作,用setInterval方法定时执行重绘是最适合的了。
    (2)特定事件重绘
      第二个方法,我们可以利用用户输入来实现操控。如果需要做一个游戏,我们可以通过监听用户交互过程中触发的事件(如document的各种keyboard,mouse事件)来控制动画效果。

    下面的例子采用第一种方式模拟一个简单的下雪场景,loop方法定义了每一帧的需要进行的操作:

    <html>
    <body>
    <canvas width="800" height="600" id="canvas"></canvas>
    <script>

    var canvas = document.getElementById('canvas');
    var particles = [];
    var tick = 0;
    function loop() {
        createParticles();
        updateParticles();
        killParticles();
        drawParticles();
    }

    function createParticles() {
        //check on every 10th tick check
        if(tick % 10 == 0) {
            if(particles.length < 100) {
                particles.push({
                        x: Math.random()*canvas.width,
                        y: 0,
                        speed: 2+Math.random()*3, //between 2 and 5
                        radius: 5+Math.random()*5, //between 5 and 10
                        color: "white",
                });
            }
        }
    }

    function updateParticles() {
        for(var i in particles) {
            var part = particles[i];
            part.y += part.speed;
        }
    }
    function killParticles() {
        for(var i in particles) {
            var part = particles[i];
            if(part.y > canvas.height) {
                part.y = 0;
            }
        }
    }

    function drawParticles() {
        var c = canvas.getContext('2d');
        c.fillStyle = "black";
        c.fillRect(0,0,canvas.width,canvas.height);
        for(var i in particles) {
            var part = particles[i];
            c.beginPath();
            c.arc(part.x,part.y, part.radius, 0, Math.PI*2);
            c.closePath();
            c.fillStyle = part.color;
            c.fill();
        }
    }

    setInterval(loop,30);

    </script>
    </body>
    </html>

    精灵动画
      精灵动画也是动画常见的技法之一。
      精灵就是一副小的图片。你可以快速的把它绘制到屏幕上。通常来说,一个精灵就是一个大图片的一部分,这部分也成为精灵片段。这个片段可能包括一个精灵的所有动作,也可能包括一个游戏中所有的角色。通过在不同的帧绘制不同的精灵,实现的动画就是精灵动画。这也是典型的翻书型的动画,大家小时候在书上肯定画过不少这种类型的动画。
      为什么要使用精灵呢?
    主要有下面一些优点:
    1.精灵是一副小图片,所以绘制速度很快。特别是相对于比较复杂的vector。
    2.重复绘制一个对象很多次时,使用精灵比较方便。比如空战游戏中的子弹,满屏幕都是,这个可以就加载一次子弹精灵,然后不断的去绘制。
    3.精灵可以快速、方便的下载。只要下载一副图片,所有的精灵就都下载下来了。这比下载很多的小图片要方便,而起内存占用量也比较小。
    4.耦合性比较低,容易升级精灵。因为你的代码只知道要不停的播放精灵,并不关心精灵的内容。这样后期想更换精灵形象的时候就很方便,只要把图片修改一下即可。
    绘制精灵的过程很简单,前面其实已经讲过了,精灵是小的图片,直接使用绘制图片的API:context.drawImage绘制即可。

    优化
      我们从上面也看到了,canvas动画在每一帧都要重画,这是很耗时的。为了提升动画效率,我们需要优化绘制过程。这里总的准则就是“少画”。

    • 不要绘制看不见的对象。
    • 使用精灵而不是大量的图形。对于于一些不会变的对象,事先使用photoshop绘制好吧。通常图片比复杂的vector绘制的更快,特别是当需要重复绘制的时候。
    • 使用缓存。可以在运行的时候创建一个看不见的canvas作为缓存。程序空闲的时候可以先绘制图形到这个缓存中,需要使用的时候,直接拷贝就可以了。这个与直接使用图片有相似的效果。
    • 使用图片拉伸实现一些特效。大多数的canvas实现都优化了图片的拉伸和切割代码,所以有些时候使用图片实现某些效果是比较快的。
    • 只重绘需要重绘的部分。
    • 减少帧数。通常帧数越高,动画越平滑,但是超过60fps也就没什么效果了,因为大多数显示器的刷新率就是60。所以选取合适的帧数即可以达到优化,有可以达到平滑的目的。
      少画就是不画,哈哈。如果背景是不变的,那么就可以放到Browser中,然后把canvas背景设置成transparent就可以了。如果有大范围的动画,那么用CSS实现吧,因为CSS是通过C代码实现的,效率比JS高。
    • 点对点绘制。如果可以,保证网格坐标系与像素坐标系一致吧,某些浏览器在这种情况下,绘制效率比较高。

    canvas基本就这些内容了,Over。

    实用参考:
    官方参考文档以及API详细说明:http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html
    权威开发入门:https://developer.mozilla.org/cn/Canvas_tutorial

    API一览表:http://simon.html5.org/dump/html5-canvas-cheat-sheet.html

  • 相关阅读:
    I/O多路复用
    Django重点之url别名
    10 个常用的 Linux 命令?
    软连接和硬链接的区别?
    Linux 重定向命令有哪些?有什么区别?
    在 linux 中 find 和 grep 的区别??
    Django中用 form 实现登录注册
    你所遵循的PEP8代码规范是什么?请举例说明其要求?
    什么是 Python 的命名空间?
    ELK+Kafka日志收集环境搭建
  • 原文地址:https://www.cnblogs.com/dxy1982/p/2391624.html
Copyright © 2011-2022 走看看