第三章:颜色与样式——让画布活起来

说实话,前两章我们画的那些矩形、路径,虽然能跑起来,但看起来就像黑白电视机时代的产物。一个真正的 Canvas 应用,颜色和样式才是灵魂。这一章,我们就来聊聊怎么给画布「上色」。

3.1 fillStyle 与 strokeStyle:最基础的上色方式

这两个属性,说白了就是「填充色」和「描边色」。我刚开始学的时候,总记混哪个是哪个。后来我给自己编了个口诀:fill 是填满,stroke 是描边

// 设置填充色为红色
ctx.fillStyle = 'red';
// 设置描边色为蓝色
ctx.strokeStyle = 'blue';

ctx.fillRect(50, 50, 100, 100);
ctx.strokeRect(50, 50, 100, 100);

嗯,这里要注意:fillStylestrokeStyle 支持的颜色格式非常丰富。除了常见的颜色名称,还有这些:

格式 示例 说明
颜色名称 'red', 'blue' 简单直观,但颜色有限
十六进制 '#ff0000' 最常用,6位或3位
RGB 'rgb(255, 0, 0)' 0-255 范围
RGBA 'rgba(255, 0, 0, 0.5)' 带透明度,最后一个参数 0-1
HSL 'hsl(0, 100%, 50%)' 色相、饱和度、亮度
我的小习惯:在项目里我一般用十六进制,因为复制粘贴方便。但做渐变色动画时,HSL 更好用——你只需要改变色相值,就能做出彩虹效果。

3.2 透明度控制:globalAlpha

有时候我们需要让图形半透明,比如做玻璃效果、阴影叠加。这时候 globalAlpha 就派上用场了。

// 设置全局透明度为 0.5
ctx.globalAlpha = 0.5;

ctx.fillStyle = 'red';
ctx.fillRect(50, 50, 100, 100);

ctx.fillStyle = 'blue';
ctx.fillRect(100, 100, 100, 100);

你想想看,两个矩形重叠的部分会变成紫色。这就是透明度叠加的效果。

我曾经踩过的坑:globalAlpha 会影响之后所有的绘制操作。如果你只想让某个图形半透明,记得画完之后把 globalAlpha 设回 1.0。不然你会发现后面的图形全都变透明了,排查半天才发现是这里的问题。

3.3 线性渐变:从一种颜色平滑过渡到另一种

渐变是 Canvas 里最常用的样式之一。线性渐变就是沿着一条直线方向,颜色逐渐变化。

// 创建线性渐变
// 参数:起点 x, 起点 y, 终点 x, 终点 y
let gradient = ctx.createLinearGradient(0, 0, 200, 0);

// 添加颜色断点
gradient.addColorStop(0, 'red');
gradient.addColorStop(0.5, 'yellow');
gradient.addColorStop(1, 'blue');

// 应用渐变
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 200);

这里的关键是 addColorStop 方法。第一个参数是位置(0 到 1 之间),第二个参数是颜色。你可以加任意多个颜色断点。

我在项目中做过一个进度条,就是用线性渐变实现的。从红色渐变到绿色,用户一看就知道进度到哪里了——红色表示刚开始,绿色表示快完成了。

3.4 径向渐变:从中心向外扩散

径向渐变和线性渐变不同,它是从一个点(或一个圆)向外扩散的。做光晕、球体效果时特别好用。

// 创建径向渐变
// 参数:起始圆 x, 起始圆 y, 起始圆半径, 结束圆 x, 结束圆 y, 结束圆半径
let gradient = ctx.createRadialGradient(100, 100, 20, 100, 100, 100);

gradient.addColorStop(0, 'white');
gradient.addColorStop(0.5, 'blue');
gradient.addColorStop(1, 'darkblue');

ctx.fillStyle = gradient;
ctx.arc(100, 100, 100, 0, Math.PI * 2);
ctx.fill();

你看,这样画出来的圆,中心是白色的,边缘是深蓝色的,看起来就像一个立体的球体。说白了,径向渐变就是模拟光线从中心向四周衰减的效果。

为什么会有两个圆?createRadialGradient 需要两个圆:起始圆和结束圆。起始圆内部的颜色是第一个颜色断点,结束圆外部的颜色是最后一个颜色断点。两个圆之间的区域就是渐变过渡区。如果起始圆半径设为 0,那就是从中心点开始渐变。

3.5 图案填充:用图片或 Canvas 作为纹理

有时候纯色和渐变还不够,我们想要用图片来填充图形。比如做一个砖墙效果、布料纹理。这时候就需要 createPattern 了。

// 创建一个图案
let img = new Image();
img.src = 'pattern.png'; // 这里需要一张图片

img.onload = function() {
  // 参数:图片对象,重复方式
  let pattern = ctx.createPattern(img, 'repeat');
  ctx.fillStyle = pattern;
  ctx.fillRect(0, 0, 400, 400);
};

重复方式有四种:

  • 'repeat':水平和垂直都重复(默认)
  • 'repeat-x':只在水平方向重复
  • 'repeat-y':只在垂直方向重复
  • 'no-repeat':不重复,只显示一次

嗯,这里要注意:createPattern 的第一个参数不一定是图片,也可以是另一个 Canvas 对象。我做过一个项目,先用 Canvas 画了一个小图案,然后用 createPattern 把它铺满整个画布——这样就不用加载外部图片了,加载速度更快。

3.6 综合示例:一个彩色仪表盘

光说不练假把式。我们来做一个综合示例,把今天学的都用上。

// 获取 Canvas 上下文
let canvas = document.getElementById('myCanvas');
let ctx = canvas.getContext('2d');

// 1. 绘制背景(线性渐变)
let bgGradient = ctx.createLinearGradient(0, 0, 0, 400);
bgGradient.addColorStop(0, '#1a1a2e');
bgGradient.addColorStop(1, '#16213e');
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, 400, 400);

// 2. 绘制仪表盘外圈(径向渐变)
let outerGradient = ctx.createRadialGradient(200, 200, 80, 200, 200, 150);
outerGradient.addColorStop(0, '#e94560');
outerGradient.addColorStop(0.7, '#0f3460');
outerGradient.addColorStop(1, '#533483');
ctx.beginPath();
ctx.arc(200, 200, 150, 0, Math.PI * 2);
ctx.fillStyle = outerGradient;
ctx.fill();

// 3. 绘制指针(带透明度)
ctx.save();
ctx.globalAlpha = 0.8;
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.moveTo(200, 200);
ctx.lineTo(200, 80);
ctx.lineTo(210, 200);
ctx.closePath();
ctx.fill();
ctx.restore();

// 4. 绘制中心点(图案填充 - 用 Canvas 生成)
let patternCanvas = document.createElement('canvas');
patternCanvas.width = 20;
patternCanvas.height = 20;
let pCtx = patternCanvas.getContext('2d');
pCtx.fillStyle = '#e94560';
pCtx.arc(10, 10, 8, 0, Math.PI * 2);
pCtx.fill();

let pattern = ctx.createPattern(patternCanvas, 'repeat');
ctx.fillStyle = pattern;
ctx.beginPath();
ctx.arc(200, 200, 20, 0, Math.PI * 2);
ctx.fill();

这个例子虽然简单,但涵盖了 fillStyle、strokeStyle、globalAlpha、线性渐变、径向渐变、图案填充这六个知识点。你想想看,如果只用纯色,这个仪表盘会多单调?

我的建议:刚开始练习时,可以试着修改渐变的位置和颜色断点,看看效果有什么不同。比如把线性渐变的终点从 (200, 0) 改成 (0, 200),颜色过渡方向就变了。多试几次,你就能掌握这些 API 的脾气了。

好了,这一章的内容就到这里。颜色和样式是 Canvas 表现力的基础,掌握了这些,你的画布就不再是黑白世界了。下一章我们会聊聊更高级的变换和动画,敬请期待。