目 录
1 色彩 Color
2 透明度 Transparency
3 线型 Line styles
4 渐变 Gradients

1. 色彩 Color

如果想要给图形上色,有两个重要的属性可以做到:fillStyle 和 strokeStyle。

  • fillStyle = color

    设置图形的填充颜色。

  • strokeStyle = color

    设置图形轮廓的颜色。

color 可以是表示 CSS 颜色值的字符串,渐变对象或者图案对象。默认情况下,线条和填充颜色都是黑色(CSS 颜色值 #000000)。

一旦设置了 strokeStyle 或者 fillStyle 的值,那么这个新值就会成为新绘制的图形的默认值。如果要给每个图形上不同的颜色,需要重新设置 fillStyle 或 strokeStyle 的值。

canvas接受所有符合 CSS3 颜色值标准 的有效字符串。

// 以下 fillStyle 的值均为 '橙色'
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)";

下面来看一个fillStyle的例子。

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  for (var i=0;i<6;i++){
    for (var j=0;j<6;j++){
      ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + 
                       Math.floor(255-42.5*j) + ',0)';
      ctx.fillRect(j*25,i*25,25,25);
    }
  }
}

在本示例里使用了两层 for 循环来绘制方格阵列,每个方格不同的颜色,结果如下图所示。

代码中使用了两个变量 i 和 j 来为每一个方格产生唯一的 RGB 色彩值,其中仅修改红色和绿色通道的值,而保持蓝色通道的值不变。

了解了fillStyle后,下面来看看strokeStyle。

function draw2() {
  var ctx = document.getElementById('cvs2').getContext('2d');
  for (var i=0;i<6;i++){
      for (var j=0;j<6;j++){
        ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' + 
                         Math.floor(255-42.5*j) + ')';
        ctx.beginPath();
        ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true);
        ctx.stroke();
      }
  }
}

与上一个例子类似,只不过本例中使用的是 strokeStyle 属性,保持不变的是红色通道值,画的不是方格,而是用 arc 方法来画圆。

2. 透明度 Transparency

除了可以绘制实色图形,还可以用 canvas 来绘制半透明的图形。通过设置 globalAlpha 属性或者使用一个半透明颜色作为轮廓或填充的样式。

  • globalAlpha = transparencyValue

这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。

globalAlpha 属性在需要绘制大量拥有相同透明度的图形时候相当高效。在绘制透明度比较多变的图形时,下面的方法可操作性更强一点。

因为 strokeStyle 和 fillStyle 属性接受符合 CSS 3 规范的颜色值,那我们可以用下面的写法来设置具有透明度的颜色。

// 指定透明颜色,用于描边和填充样式
ctx.strokeStyle = "rgba(255,0,0,0.5)";
ctx.fillStyle = "rgba(255,0,0,0.5)";

rgba() 方法与 rgb() 方法类似,就多了一个用于设置色彩透明度的参数。它的有效范围是从 0.0(完全透明)到 1.0(完全不透明)。

下面来分别看看两种方式的示例。

function draw3() {
  var ctx = document.getElementById('cvs3').getContext('2d');
  ctx.fillStyle = '#FD0';
  ctx.fillRect(0,0,75,75);
  ctx.fillStyle = '#6C0';
  ctx.fillRect(75,0,75,75);
  ctx.fillStyle = '#09F';
  ctx.fillRect(0,75,75,75);
  ctx.fillStyle = '#F30';
  ctx.fillRect(75,75,75,75);
  ctx.fillStyle = '#FFF';

  // 设置透明度值
  ctx.globalAlpha = 0.2;

  // 画半透明圆
  for (var i=0;i<7;i++){
      ctx.beginPath();
      ctx.arc(75,75,10+10*i,0,Math.PI*2,true);
      ctx.fill();
  }
}

先来看看globalAlpha。

在本例中,将用四色格作为背景,设置 globalAlpha 为 0.2 后,在上面画一系列半径递增的半透明圆。最终结果是一个径向渐变效果。圆叠加得越更多,原先所画的圆的透明度会越低。通过增加循环次数,画更多的圆,背景图的中心部分会完全消失。

下面是rgba()的示例。

function draw4() {
  var ctx = document.getElementById('cvs4').getContext('2d');
  ctx.fillStyle = 'rgb(255,221,0)';
  ctx.fillRect(0,0,150,37.5);
  ctx.fillStyle = 'rgb(102,204,0)';
  ctx.fillRect(0,37.5,150,37.5);
  ctx.fillStyle = 'rgb(0,153,255)';
  ctx.fillRect(0,75,150,37.5);
  ctx.fillStyle = 'rgb(255,51,0)';
  ctx.fillRect(0,112.5,150,37.5);

  // 画半透明矩形
  for (var i=0;i<10;i++){
    ctx.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')';
    for (var j=0;j<4;j++){
      ctx.fillRect(5+i*14,5+j*37.5,14,27.5)
    }
  }
}

与前一个例子类似,不过不是画圆,而是画矩形。这里还可以看出,rgba() 可以分别设置轮廓和填充样式,因而具有更好的可操作性和使用灵活性。

3. 线型 Line styles

上一篇博文中的线条十分单调,这里将介绍一系列属性用于修饰线条的样式。

  • lineWidth = value

    设置线条宽度。

  • lineCap = type

    设置线条末端样式。

  • lineJoin = type

    设定线条与线条间接合处的样式。

  • getLineDash() 返回一个包含当前虚线样式,长度为非负偶数的数组。

  • setLineDash(segments)

    设置当前虚线样式。

  • lineDashOffset = value

    设置虚线样式的起始偏移量。

下面来分别看看各属性的例子,首先是lineWidth。

function draw5() {
  var ctx = document.getElementById('cvs5').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属性设置当前绘线的粗细。属性值必须为正数。默认值是1.0。

lineWidth是指给定路径的中心到两边的粗细。换句话说就是在路径的两边各绘制线宽的一半。因为画布的坐标并不和像素直接对应,当需要获得精确的水平或垂直线的时候要特别注意。

本例中使用递增的宽度绘制了10条直线。最左边的线宽1.0单位。

最左边的以及所有宽度为奇数的线颜色出现偏差,这就是因为路径的定位问题导致。

想要获得精确的线条,就得知道线条是如何描绘出来。

下图用网格来代表 canvas 的坐标格,每一格对应屏幕上一个像素点。在第一个图中,填充了 (2,1) 至 (5,5) 的矩形,整个区域的边界刚好落在像素边缘上,这样就可以得到的矩形有着清晰的边缘。

img1

如果想要绘制一条从 (3,1) 到 (3,5),宽度是 1.0 的线条,结果将会是第二幅图。

实际填充区域(深蓝色部分)仅仅延伸至路径两旁各一半像素。而另外半个像素会以笔触颜色一半的色调进行渲染,于是线条颜色被渲染为浅蓝和深蓝两个部分。这就是上例中为何宽度为 1.0 的线并不准确的原因。

要解决这个问题,必须对路径施以更加精确的控制。已知粗 1.0 的线条会在路径两边各延伸半像素,那么像第三幅图那样绘制从 (3.5,1) 到 (3.5,5) 的线条,其边缘正好落在像素边界,填充出来就是准确的宽为 1.0 的线条。

以上是lineWidth,下面来看看lineCap属性。

function draw6() {
  var ctx = document.getElementById('cvs6').getContext('2d');
  var lineCap = ['butt','round','square'];
  ctx.strokeStyle = '#09f';
  ctx.beginPath();
  ctx.moveTo(10,10);
  ctx.lineTo(140,10);
  ctx.moveTo(10,140);
  ctx.lineTo(140,140);
  ctx.stroke();

  // 画线条
  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。默认是 butt。

本例中绘制了三条直线,分别赋予不同的 lineCap 值。还有两条辅助线是为了看清它们之间的区别,三条线的起点终点都落在辅助线上。

最左边的线用了默认的 butt 。可以注意到它是与辅助线齐平的。中间的是 round 的效果,端点处加上了半径为一半线宽的半圆。右边的是 square 的效果,端点处加上了等宽且高度为一半线宽的方块。

接下来是lineJoin属性。

function draw7() {
  var ctx = document.getElementById('cvs7').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 的效果。当值是 miter 的时候,线段会在连接处外侧延伸直至交于一点。

最后介绍虚线的使用。

var canvas8 = document.getElementById('cvs8');
var ctx8 = document.getElementById('cvs8').getContext('2d');
var offset = 0;
function draw8() {
  ctx8.clearRect(0,0, canvas8.width, canvas8.height);
  ctx8.setLineDash([4, 2]);
  ctx8.lineDashOffset = -offset;
  ctx8.strokeRect(10,10, 100, 100);
}

function march() {
  offset++;
  if (offset > 16) {
    offset = 0;
  }
  draw8();
  setTimeout(march, 20);
}
march();

用 setLineDash 方法和 lineDashOffset 属性来制定虚线样式。

setLineDash 方法接受一个数组,来指定线段与间隙的交替,数组第一个值表示虚线的长度,第二个值表示虚线之前的空隙。

lineDashOffset 属性设置起始偏移量。

4. 渐变 Gradients

与CSS3和SVG一样,canvas也有自己的渐变功能,同样分为 线性渐变 和 径向渐变。

使用下面的方法可以新建一个 canvasGradient 对象,并且赋给图形的 fillStyle 或 strokeStyle 属性,从而实现渐变效果。

  • createLinearGradient(x1, y1, x2, y2)

    createLinearGradient 线性渐变方法接受 4 个参数,表示渐变的起点 (x1,y1) 与终点 (x2,y2)。

  • createRadialGradient(x1, y1, r1, x2, y2, r2)

    createRadialGradient 径向渐变方法接受 6 个参数,前三个定义一个以 (x1,y1) 为原点,半径为 r1 的圆,后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆。

var lineargradient = ctx.createLinearGradient(0,0,150,150);
var radialgradient = ctx.createRadialGradient(75,75,0,75,75,100);

创建出 canvasGradient 对象后,可以用 addColorStop 方法上色。

  • gradient.addColorStop(position, color)

    addColorStop 方法接受 2 个参数,position 参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。例如,0.5 表示颜色会出现在正中间。color 参数必须是一个有效的 CSS 颜色值(如 #FFF, rgba(0,0,0,1),等等)。

可以根据需要添加任意多个色标(color stops)。下面是最简单的线性黑白渐变的例子。

function draw9() {
  var ctx = document.getElementById('cvs9').getContext('2d');
  var lineargradient = ctx.createLinearGradient(0,0,130,130);
  lineargradient.addColorStop(0,'white');
  lineargradient.addColorStop(0.5,'grey');
  lineargradient.addColorStop(1,'black');
  
  ctx.fillStyle = lineargradient;
  // draw shapes
  ctx.fillRect(10,10,130,130);
}

然后来看看径向渐变。

function draw10() {
  var ctx = document.getElementById('cvs10').getContext('2d');
  var radgrad = ctx.createRadialGradient(45,45,10,52,50,30);
  radgrad.addColorStop(0, '#A7D30C');
  radgrad.addColorStop(0.9, '#019F62');
  radgrad.addColorStop(1, 'rgba(1,159,98,0)');
  
  var radgrad2 = ctx.createRadialGradient(105,105,20,112,120,50);
  radgrad2.addColorStop(0, '#FF5F98');
  radgrad2.addColorStop(0.75, '#FF0188');
  radgrad2.addColorStop(1, 'rgba(255,1,136,0)');

  var radgrad3 = ctx.createRadialGradient(95,15,15,102,20,40);
  radgrad3.addColorStop(0, '#00C9FF');
  radgrad3.addColorStop(0.8, '#00B5E2');
  radgrad3.addColorStop(1, 'rgba(0,201,255,0)');

  var radgrad4 = ctx.createRadialGradient(0,150,50,0,140,90);
  radgrad4.addColorStop(0, '#F4F201');
  radgrad4.addColorStop(0.8, '#E4C700');
  radgrad4.addColorStop(1, 'rgba(228,199,0,0)');
  
  // 画图形
  ctx.fillStyle = radgrad4;
  ctx.fillRect(0,0,150,150);
  ctx.fillStyle = radgrad3;
  ctx.fillRect(0,0,150,150);
  ctx.fillStyle = radgrad2;
  ctx.fillRect(0,0,150,150);
  ctx.fillStyle = radgrad;
  ctx.fillRect(0,0,150,150);
}

本例定义了4个不同的径向渐变。由于可以控制渐变的起始与结束点,所以可以实现稍微复杂的效果。

比如这里让起点稍微偏离终点,这样达到了一种球状3D效果,但最好不要让里圆与外圆部分交叠。

样式的介绍就到这里,下一篇博文将介绍文本的绘制和图片的使用。