花了半个月时间看完一遍《SVG Essentials》后,再看《HTML5+canvas基础教程》感觉大部分内容都比较轻松,很多canvas的方法和svg的元素属性之间有着及其相似的功能。

前段时间一直忙项目,外加上看canvas基础教程花了不到一周时间,没来得及记录整理。最近稍微闲点儿了,又找了本《HTML5 Canvas核心技术 图形、动画与游戏开发》看,感觉比前一本更详细些,两本结合着再研究研究,顺便整理成文。

Canvas与SVG的异同

这两种绘图技术本质上完全不同。
canvas本质是对像素的处理,用canvas画条直线时,canvas本身不知道这是条直线,只知道一组特定位置的像素的颜色被改变了。canvas可以知道每一个像素的颜色信息(rgba),其非常擅长对图片、甚至是对视频的处理,在网页游戏中canvas应用的极多,对这点svg望洋兴叹。
svg本质是对矢量的处理,svg知道其画布中所有元素的具体信息,比如一根直线的起点和终点坐标、粗细、颜色等等,可以非常轻易的控制其内部所有元素的变化。因为矢量的性质,svg绘制的图可以做到无损放大缩小,对这点canvas不服不行。

即便如此,两种绘图技术可以实现的功能重合度依然非常高。以我目前的了解,两种技术在数据可视化这块上的表现基本上没差,一种可以实现的,换另外一种同样可以实现。基于svg的d3.js,hCharts,和基于canvas的echarts都在数据可视化这块表现得非常漂亮。两者也都有自己一套实现动画的实现。svg利用clip、mask、filter也可以对图片做非常丰富的处理,完全不输canvas。

所以选择使用哪种技术,应该比较好区别。对于绘图缩放有要求的用svg,对复杂图形处理有要求的用canvas。其他情况看个人喜好。无论选哪种,js基础都必须过硬(svg需要用js实现与图形之间的交互,而canvas所有绘图工作都是用js来编写的)

Canvas中的viewBox

svg里有个viewBox这一重要概念(详见svg之viewBox),canvas里也有。
canvas绘制画布的时候需要指定canvas元素的width和height,这两个属性都是没有单位的。

<canvas id="canvas" width="300" height="150"></canvas>

默认情况下,会绘制出一个300px*150px大小的画布区域。
在画布区域内做的任何绘图操作,涉及到长度、坐标数值的时候,一律都没有单位。

var context = document.getElementById("canvas").getContext("2d");
context.fillRect(40, 20, 200, 100);

这时绘制出的是一个左上角坐标为(40px, 20px),长宽为200px*100px的长方形。

但如果给这个<canvas>元素加个样式:style="width:600px; height:300px",会发现画布大小会变成600px*300px,而画布内的长方形也被同比放大了。
所以定义在<canvas>元素上的width和height属性就是其viewBox的宽高。

另外,当canvas的width和height与其style的width和height不等时,如果需要获取鼠标点击事件触发时,鼠标在canvas坐标系中的坐标位置,可以这样:

function windowToCanvas(canvas, x, y) {
    var box = canvas.getBoundingClientRect();
    return { 
        x: (x - box.left) * (canvas.width  / box.width),
        y: (y - box.top)  * (canvas.height / box.height)
    };
}

canvas.addEventListener("click", function(e){
    var canvasPoint = windowToCanvas(canvas, e.clientX, e.clientY),
        x = canvasPoint.x,
        y = canvasPoint.y;
})

context

<canvas>元素本身就三个方法:

方法名 描述
getContext() 返回与该canvas元素相关的绘图环境对象。最常用的即为“2d”,表示二维绘图环境。
toDataUrl(type, quality) 返回一个数据地址url,可以被设置成img的src属性值,type为图像类型(image/jpg, image/png等);quality必须为0~1之间的double数值,表示图像压缩质量。
toBlob(callback, type, quality...) 创建一个用于表示该canvas元素图像文件的blob。callback为回调函数,回调函数的参数为一个指向blob的引用。

由canvas.getContext("2d")返回的对象即为该canvas的二维绘图环境对象CanvasRenderingContext2D的实例context。
所有的绘图操作都是通过设置context的属性和调用context提供的方法来实现的。
canvas的绘图过程简单点讲有点类似画水彩画:选择好笔的粗细和沾的颜料,然后画几笔,再换支笔沾点别的颜料,再画几笔。设置context的属性就是选择笔的粗细和颜料,调用context的方法就是画几笔。

context属性

context包含的属性如下:

属性名 描述
canvas 指向所属的canvas对象。常用来获取canvas的宽高:context.canvas.width, context.canvas.height
fillStyle 指定后续图形填充色、渐变色或者图案
strokeStyle 指定后续对路径进行描边的颜色、渐变色或者图案
font 指定后续fillText()或strokeText()方法所绘制的文字的字体
textAlign 指定后续fillText()或strokeText()方法所绘制的文字的水平对齐方式
textBaseLine 指定后续fillText()或strokeText()方法所绘制的文字的垂直对齐方式
globalAlpha 指定后续图形的透明度,值域为[0,1]
globalCompositeOperation 指定后续的图形与画布上已有图形的覆盖方式
lineCap 指定后续线段端点的呈现方式(butt、round、square),默认为butt
lineWidth 指定后续线段宽度,默认为1
lineJoin 指定后续线段与线段之间连接点的呈现方式(bevel、round、miter),默认为miter
miterLimit 指定后续线段与线段之间的miter连接点最长距离
shadowBlur 指定后续图形的阴影扩散范围,值越大扩散范围越大,默认为0(该值不是阴影长度,代表的是高斯模糊方程式中的参数值)
shadowColor 指定后续图形阴影的颜色。半透明阴影色可将其下面的元素显示出来
shadowOffsetX 指定后续图形阴影的水平偏移量
shadowOffsetY 指定后续图形阴影的垂直偏移量

(熟悉svg的话,应该能找到非常多的共通之处)

context.save() 和 context.restore()

现在考虑这样一种情况:用调了半天的颜色A画了几笔,然后再调出颜色B画了几笔,当又需要换回颜色A的时候,最好的做法当然是在调色板里保留好每次调好的颜色,而不必每次都重新调色。

context的save()和restore()方法可以实现类似功能。
当设置好一些context的属性,执行context.save()可以将当前的context属性状态保存到一个栈中,这样一来,不管之后context属性被改成啥样,只要调用一次context.restore(),context的属性即可恢复到之前执行context.save()时的状态,这个属性状态会出栈。
既然是将属性状态保存到一个栈中,就可以多次执行save()和restore()方法,而save()和restore()在调用时会遵循先进后出的原则来保存和还原context的属性状态。

当然这种模式并没有像使用调色板那么方便,无法任意切换到某个属性状态,所以在使用时和用调色板时候的思路还是不太一样的。比如在状态A时执行save(),再改成状态B,此时如果想利用restore()切换回状态A,则B状态只能被丢弃。


梦梦她爹
1.8k 声望122 粉丝