欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

突袭HTML5之Canvas 2D入门5-事件与动画

程序员文章站 2022-05-17 23:52:13
Canvas & SVG & DOM   Canvas与SVG都是2D绘图的利器,除此之外,使用CSS、DOM也可以实现某些性状的绘制,而且在动画中,也都可以使用...

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。

 

摘自  沙场秋点兵