canvas元素简易教程(7)(大部分转自火狐,自己只写了简单的代码分析)
上次我们一起学习了canvas对于颜色与透明的调整,今天我们来学习一下线型、渐变、图案与阴影的相关知识。
首先是线型,在canvas对象中我们可以用一系列的属性来控制线的样式。
lineWidth = value
lineCap = type
lineJoin = type
miterLimit = value
我觉得用例子来讲解要比单纯的介绍好得多,这也是我一贯的风格。让我们先上代码:
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();
}
}
这个例子绘制了十条粗细不一的线,那么就让我们来熟悉一下这个属性:lineWidth。
lineWidth设置当前绘线的粗细,属性值必须为正数,默认为1.0.
线宽是指给定路径的中心到两边的粗细。换句话说就是在路径的两边各绘制线宽的一半。因为画布的坐标并不和像素直接对应,当需要获得精确的水平或垂直线的时候要特别注意。
例子中最左边的以及所有宽度为奇数的线并不能精确呈现,这就是因为路径的定位问题。
想要获得精确的线条,必须对线条是如何描绘出来的有所理解。见下图,用网格来代表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。
无例子,无学习。来,让我们上代码:
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();
}
}
来,我们分析一下代码。前半段绘制了两条辅助线,目的是让大家看清楚这个属性赋不同值的区别。然后后半段用循环来遍历了lineCap的所有参数项并一一赋值。三条线的起点和重点都落在辅助线之中。
最左边的线用了默认的butt。可以注意到它是与辅助线齐平的。中间的是round的效果,端点处加上了半径为一半线宽的半圆。右边的是square的效果,端点出加上了等宽且高度为一半线宽的方块。
现在对于直线我们掌握的差不多了,但是新的问题又出来了。我们怎么去绘制两条线段连接的地方呢?是圆角弧还是直角?还是别的什么?用路径方法去描边填充?当然不是。让我们来学习这个新的属性:lineJoin
先来一段代码吧,大量代码是知识的基础嘛:
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();
}
}
lineJoin的属性值决定了图形中两线段连接处所显示的样子。它可以是这三种之一:round,bevel和miter。默认是miter。
在这个例子中,我们定义了三个连接方式,分别设置了不同的lineJoin值。第一次是round的效果,角被磨成了圆角弧。第二次是bevel效果,角变成了平的。第三次是miter的效果,线段延伸直到交于一点,延伸效果受到下面将要介绍的miterLimit属性的制约。
miterLimit又是干啥的呢?就像刚才看到的milter的效果,线段向外延伸交汇在一起。这俩线段之间的夹角至关重要,要是角度较大,好的,出去没多远俩就能遇上。但是要是角度比较小的话,那就麻烦咯,延长的长度将承指数级上升。。。
miterLimit属性就是用来设定外延交点与连接点的最大距离,如果交点距离大于此值,连接效果会变成了bevel。
老规矩,来代码吧:
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// Clear canvas
ctx.clearRect(0,0,150,150);
// Draw guides
ctx.strokeStyle = '#09f';
ctx.lineWidth = 2;
ctx.strokeRect(-5,50,160,50);
// Set line styles
ctx.strokeStyle = '#000';
ctx.lineWidth = 10;
//Set miterLimit value
ctx.miterLimit = 10;
// Draw lines
ctx.beginPath();
ctx.moveTo(0,100);
for (i=0;i<24;i++){
var dy = i%2==0 ? 25 : -25 ;
ctx.lineTo(Math.pow(i,1.5)*2,75+dy);
}
ctx.stroke();
return false;
}
这个例子我做了点小小的改动,原例是由input标签输入miterLimit属性,我这里为了方便大家学习,直接写死了,望谅解。
大家可以更改这个数值,自己看一下变化,相信就会对这个简单的属性有一个直接的认识了。
今天我们的学习就先到这里吧,下次我们将进行渐变的学习,我们下次见~