canvas 绘图操作
Canvas 2D 上下文提供了丰富的绘图方法,可以绘制矩形、线条、弧线、文字,以及更复杂的路径和形状。本节将通过实际示例介绍 canvas 的基础绘图操作。
前置知识
阅读本节前,建议先了解:canvas 绑定与上下文
基础概念
Canvas 2D 绘图基于状态机模型:通过设置填充颜色、描边颜色、线宽等状态,然后调用绘图方法在画布上绘制内容。所有绘图操作都通过 CanvasRenderingContext2D 对象的方法完成。
基本绘图类型包括:矩形(最简单)、路径(线条、弧线、曲线)、文字、图像和像素操作。
语法
绘制矩形
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>绘制矩形</title>
</head>
<body>
<h1>Canvas 矩形绘制</h1>
<canvas id="rectCanvas" width="500" height="300"
style="border: 1px solid #ccc;"></canvas>
<script>
var ctx = document.getElementById('rectCanvas').getContext('2d');
// fillRect(x, y, width, height):填充矩形
ctx.fillStyle = '#4285f4';
ctx.fillRect(20, 20, 150, 100);
// strokeRect(x, y, width, height):描边矩形
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 3;
ctx.strokeRect(200, 20, 150, 100);
// clearRect(x, y, width, height):清除区域
ctx.fillStyle = '#2ecc71';
ctx.fillRect(380, 20, 100, 100);
ctx.clearRect(400, 40, 60, 60); // 清除中间部分
// 半透明矩形
ctx.fillStyle = 'rgba(255, 165, 0, 0.6)';
ctx.fillRect(50, 150, 120, 80);
ctx.fillStyle = 'rgba(0, 128, 255, 0.6)';
ctx.fillRect(100, 180, 120, 80);
// 圆角矩形(使用 roundRect)
ctx.beginPath();
ctx.roundRect(280, 150, 150, 80, [15]);
ctx.fillStyle = '#9b59b6';
ctx.fill();
</script>
</body>
</html>详细说明
绘制线条和路径
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>线条与路径</title>
</head>
<body>
<h1>线条与路径</h1>
<canvas id="lineCanvas" width="500" height="300"
style="border: 1px solid #ccc;"></canvas>
<script>
var ctx = document.getElementById('lineCanvas').getContext('2d');
// 直线
ctx.beginPath();
ctx.moveTo(20, 20); // 起点
ctx.lineTo(200, 100); // 终点
ctx.strokeStyle = '#4285f4';
ctx.lineWidth = 3;
ctx.stroke();
// 折线
ctx.beginPath();
ctx.moveTo(20, 150);
ctx.lineTo(100, 50);
ctx.lineTo(180, 150);
ctx.lineTo(260, 50);
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 2;
ctx.stroke();
// 三角形(闭合路径)
ctx.beginPath();
ctx.moveTo(300, 200);
ctx.lineTo(400, 50);
ctx.lineTo(500, 200);
ctx.closePath(); // 自动闭合路径
ctx.fillStyle = '#2ecc71';
ctx.fill();
ctx.strokeStyle = '#27ae60';
ctx.lineWidth = 2;
ctx.stroke();
// 虚线
ctx.beginPath();
ctx.setLineDash([10, 5]); // 10px 实线,5px 间隔
ctx.moveTo(20, 250);
ctx.lineTo(480, 250);
ctx.strokeStyle = '#666';
ctx.lineWidth = 2;
ctx.stroke();
ctx.setLineDash([]); // 重置虚线
</script>
</body>
</html>绘制弧线和圆形
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>弧线与圆形</title>
</head>
<body>
<h1>弧线与圆形</h1>
<canvas id="arcCanvas" width="500" height="300"
style="border: 1px solid #ccc;"></canvas>
<script>
var ctx = document.getElementById('arcCanvas').getContext('2d');
// 完整圆形:arc(x, y, radius, startAngle, endAngle)
ctx.beginPath();
ctx.arc(80, 80, 50, 0, Math.PI * 2);
ctx.fillStyle = '#4285f4';
ctx.fill();
// 描边圆形
ctx.beginPath();
ctx.arc(220, 80, 50, 0, Math.PI * 2);
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 3;
ctx.stroke();
// 半圆弧
ctx.beginPath();
ctx.arc(360, 80, 50, 0, Math.PI);
ctx.strokeStyle = '#2ecc71';
ctx.lineWidth = 3;
ctx.stroke();
// 扇形
ctx.beginPath();
ctx.moveTo(150, 200); // 圆心
ctx.arc(150, 200, 60, -Math.PI / 4, Math.PI / 2);
ctx.closePath();
ctx.fillStyle = '#f39c12';
ctx.fill();
// 椭圆 ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle)
ctx.beginPath();
ctx.ellipse(350, 200, 80, 40, 0, 0, Math.PI * 2);
ctx.fillStyle = '#9b59b6';
ctx.fill();
</script>
</body>
</html>绘制文字
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>绘制文字</title>
</head>
<body>
<h1>Canvas 文字</h1>
<canvas id="textCanvas" width="500" height="300"
style="border: 1px solid #ccc;"></canvas>
<script>
var ctx = document.getElementById('textCanvas').getContext('2d');
// fillText(text, x, y):填充文字
ctx.fillStyle = '#333';
ctx.font = '32px sans-serif';
ctx.fillText('Hello Canvas!', 20, 50);
// strokeText(text, x, y):描边文字
ctx.strokeStyle = '#4285f4';
ctx.lineWidth = 2;
ctx.font = 'bold 40px serif';
ctx.strokeText('描边文字', 20, 110);
// 文字对齐
ctx.fillStyle = '#e74c3c';
ctx.font = '18px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('居中对齐', 250, 170);
ctx.textAlign = 'right';
ctx.fillText('右对齐', 480, 200);
ctx.textAlign = 'left';
ctx.fillText('左对齐(默认)', 20, 230);
// 文字基线
ctx.fillStyle = '#2ecc71';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('垂直居中', 250, 270);
</script>
</body>
</html>颜色与样式
javascript
var ctx = canvas.getContext('2d');
// 填充颜色
ctx.fillStyle = '#ff0000'; // 十六进制
ctx.fillStyle = 'rgb(255, 0, 0)'; // RGB
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; // RGBA(半透明)
ctx.fillStyle = 'red'; // 颜色名称
// 描边颜色
ctx.strokeStyle = '#0000ff';
// 线宽
ctx.lineWidth = 3;
// 线条端点
ctx.lineCap = 'round'; // round, butt, square
// 线条连接
ctx.lineJoin = 'round'; // round, bevel, miter
// 渐变填充
var gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, '#ff0000');
gradient.addColorStop(1, '#0000ff');
ctx.fillStyle = gradient;实战示例
绘制简单图表
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas 柱状图</title>
<style>
canvas { border-radius: 8px; }
</style>
</head>
<body>
<h1>Canvas 柱状图</h1>
<canvas id="chartCanvas" width="500" height="350"
style="border: 1px solid #ddd;"></canvas>
<script>
var canvas = document.getElementById('chartCanvas');
var ctx = canvas.getContext('2d');
var data = [
{ label: '一月', value: 120, color: '#4285f4' },
{ label: '二月', value: 200, color: '#34a853' },
{ label: '三月', value: 150, color: '#fbbc05' },
{ label: '四月', value: 280, color: '#ea4335' },
{ label: '五月', value: 220, color: '#673ab7' },
];
var maxVal = 300;
var barWidth = 50;
var startX = 80;
var startY = 280;
var chartHeight = 230;
// 背景
ctx.fillStyle = '#fafafa';
ctx.fillRect(0, 0, 500, 350);
// 网格线
ctx.strokeStyle = '#eee';
ctx.lineWidth = 1;
for (var i = 0; i <= 3; i++) {
var y = startY - (chartHeight / 3) * i;
ctx.beginPath();
ctx.moveTo(startX, y);
ctx.lineTo(460, y);
ctx.stroke();
ctx.fillStyle = '#999';
ctx.font = '12px sans-serif';
ctx.textAlign = 'right';
ctx.fillText(Math.round(maxVal / 3 * i), startX - 10, y + 4);
}
// 柱状
data.forEach(function(item, index) {
var x = startX + index * 75;
var height = (item.value / maxVal) * chartHeight;
var y = startY - height;
// 柱子
ctx.fillStyle = item.color;
ctx.fillRect(x, y, barWidth, height);
// 数值标签
ctx.fillStyle = '#333';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(item.value, x + barWidth / 2, y - 8);
// X 轴标签
ctx.fillStyle = '#666';
ctx.font = '13px sans-serif';
ctx.fillText(item.label, x + barWidth / 2, startY + 25);
});
</script>
</body>
</html>注意事项
- canvas 绘图是立即模式(Immediate Mode),绘制后无法修改(只能重新绘制)
- 路径操作必须以
beginPath()开始 closePath()会自动连接起点和终点- 文字绘制不支持自动换行
- 颜色值使用 CSS 标准格式
最佳实践
- 使用
save()/restore()保存和恢复绘图状态 - 复杂场景使用离屏 canvas 进行缓存
- 对于动画,使用
requestAnimationFrame - 封装常用绘图操作为可复用函数
- 避免在循环中创建渐变对象
下一节
继续学习:SVG 内联矢量图