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.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方法定义了每一帧的需要进行的操作:
<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