线型 Line styles
可以通过一系列属性来设置线的样式。
lineWidth = value lineCap = type lineJoin = type miterLimit = value
我会详细介绍这些属性,不过通过以下的样例可能会更加容易理解。
lineWidth 属性的例子
这 个属性设置当前绘线的粗细。属性值必须为正数。默认值是1.0。线宽是指给定路径的中心到两边的粗细。换句话说就是在路径的两边各绘制线宽的一半。因为画 布的坐标并不和像素直接对应,当需要获得精确的水平或垂直线的时候要特别注意。在下面的例子中,用递增的宽度绘制了10条直线。最左边的线宽1.0单位。 并且,最左边的以及所有宽度为奇数的线并不能精确呈现,这就是因为路径的定位问题。
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); for (var i = 0; i < 10; i++){ ctx.lineWidth = 1+i; ctx.beginPath(); ctx.moveTo(5+i*14,5); ctx.lineTo(5+i*14,140); ctx.stroke(); } }
想 要获得精确的线条,必须对线条是如何描绘出来的有所理解。见下图,用网格来代表 canvas 的坐标格,每一格对应屏幕上一个像素点。在第一个图中,填充了 (2,1) 至 (5,5) 的矩形,整个区域的边界刚好落在像素边缘上,这样就可以得到的矩形有着清晰的边缘。
如 果你想要绘制一条从 (3,1) 到 (3,5),宽度是 1.0 的线条,你会得到像第二幅图一样的结果。实际填充区域(深蓝色部分)仅仅延伸至路径两旁各一半像素。而这半个像素又会以近似的方式进行渲染,这意味着那些 像素只是部分着色,结果就是以实际笔触颜色一半色调的颜色来填充整个区域(浅蓝和深蓝的部分)。这就是上例中为何宽度为 1.0 的线并不准确的原因。要解决这个问题,你必须对路径施以更加精确的控制。已知粗 1.0 的线条会在路径两边各延伸半像素,那么像第三幅图那样绘制从 (3.5,1) 到 (3.5,5) 的线条,其边缘正好落在像素边界,填充出来就是准确的宽为 1.0 的线条。对于那些宽度为偶数的线条,每一边的像素数都是整数,那么你想要其路径是落在像素点之间 (如那从 (3,1) 到 (3,5)) 而不是在像素点的中间。同样,注意到那个例子的垂直线条,其 Y 坐标刚好落在网格线上,如果不是的话,端点上同样会出现半渲染的像素点。虽然开始处理可缩放的 2D 图形时会有点小痛苦,但是及早注意到像素网格与路径位置之间的关系,可以确保图形在经过缩放或者其它任何变形后都可以保持看上去蛮好:线宽为 1.0 的垂线在放大 2 倍后,会变成清晰的线宽为 2.0,并且出现在它应该出现的位置上。
lineCap
属性的例子
属性
lineCap的指决定了线段端点显示的样子。它可以为下面的三种的其中之一:butt,round和square。默认是butt。
这个例子里面,我绘制了三条直线,分别赋予不同的lineCap值。还有两条辅助线,为了可以看得更清楚它们之间的区别,三条线的起点终点都落在辅助线上。最左边的线用了默认的butt。可以注意到它是与辅助线齐平的。中间的是round的效果,端点处加上了半径为一半线宽的半圆。右边的是square的效果,端点出加上了等宽且高度为一半线宽的方块。
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); var lineCap = ['butt','round','square']; // Draw guides ctx.strokeStyle = '#09f'; ctx.beginPath(); ctx.moveTo(10,10); ctx.lineTo(140,10); ctx.moveTo(10,140); ctx.lineTo(140,140); ctx.stroke(); // Draw lines ctx.strokeStyle = 'black'; for (var i=0;i<lineCap.length;i++){ ctx.lineWidth = 15; ctx.lineCap = lineCap[i]; ctx.beginPath(); ctx.moveTo(25+i*50,10); ctx.lineTo(25+i*50,140); ctx.stroke(); } }
lineJoin
属性的例子
lineJoin的属性值决定了图形中两线段连接处所显示的样子。它可以是这三种之一:round,bevel和miter。默认是miter。
这里我同样用三条折线来做例子,分别设置不同的lineJoin值。最上面一条是round的效果,边角处被磨圆了,圆的半径等于线宽。中间和最下面一条分别是 bevel 和 miter的效果。当值是miter的时候,线段会在连接处外侧延伸直至交于一点,延伸效果受到下面将要介绍的miterLimit属性的制约。
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); var lineJoin = ['round','bevel','miter']; ctx.lineWidth = 10; for (var i=0;i<lineJoin.length;i++){ ctx.lineJoin = lineJoin[i]; ctx.beginPath(); ctx.moveTo(-5,5+i*40); ctx.lineTo(35,45+i*40); ctx.lineTo(75,5+i*40); ctx.lineTo(115,45+i*40); ctx.lineTo(155,5+i*40); ctx.stroke(); } }
miterLimit
属性的演示例子
就如上一个例子所见的应用miter
的效果,线段的外侧边缘会延伸交汇于一点上。线段直接夹角比较大的,交点不会太远,但当夹角减少时,交点距离会呈指数级增大。miterLimit
属性就是用来设定外延交点与连接点的最大距离,如果交点距离大于此值,连接效果会变成了 bevel。
渐变 Gradients
就好像一般的绘图软件一样,我们可以用线性或者径向的渐变来填充或描边。我们用下面的方法新建一个canvasGradient
对象,并且赋给图形的fillStyle或strokeStyle
属性。
createLinearGradient(x1,y1,x2,y2) createRadialGradient(x1,y1,r1,x2,y2,r2)
createLinearGradient方法接受 4 个参数,表示渐变的起点 (x1,y1) 与终点 (x2,y2)。createRadialGradient方法接受 6 个参数,前三个定义一个以 (x1,y1) 为原点,半径为 r1 的圆,后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆。
var lineargradien