1. 矩形绘制
1.1 基础矩形方法
Canvas提供了三个基础的矩形绘制方法:
// 获取canvas和上下文
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 1. fillRect() - 绘制填充矩形
ctx.fillStyle = 'red';
ctx.fillRect(50, 50, 100, 80); // x, y, width, height
// 2. strokeRect() - 绘制矩形边框
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;
ctx.strokeRect(200, 50, 100, 80);
// 3. clearRect() - 清除矩形区域
ctx.clearRect(75, 75, 50, 30);
1.2 矩形样式设置
// 填充样式
ctx.fillStyle = '#ff6b6b'; // 十六进制颜色
ctx.fillStyle = 'rgb(255, 107, 107)'; // RGB颜色
ctx.fillStyle = 'rgba(255, 107, 107, 0.5)'; // RGBA颜色
ctx.fillStyle = 'hsl(0, 100%, 71%)'; // HSL颜色
// 描边样式
ctx.strokeStyle = '#4ecdc4';
ctx.lineWidth = 3;
ctx.lineCap = 'round'; // 线条端点样式: butt, round, square
ctx.lineJoin = 'round'; // 线条连接样式: miter, round, bevel
// 虚线样式
ctx.setLineDash([5, 10]); // 设置虚线模式
ctx.lineDashOffset = 0; // 虚线偏移
1.3 渐变填充矩形
// 线性渐变
function drawLinearGradientRect() {
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, '#ff6b6b');
gradient.addColorStop(0.5, '#4ecdc4');
gradient.addColorStop(1, '#45b7d1');
ctx.fillStyle = gradient;
ctx.fillRect(50, 150, 200, 100);
}
// 径向渐变
function drawRadialGradientRect() {
const gradient = ctx.createRadialGradient(150, 200, 0, 150, 200, 100);
gradient.addColorStop(0, '#ff6b6b');
gradient.addColorStop(1, '#4ecdc4');
ctx.fillStyle = gradient;
ctx.fillRect(100, 150, 100, 100);
}
drawLinearGradientRect();
drawRadialGradientRect();
2. 圆形与弧形绘制
2.1 基础圆形绘制
// arc() 方法绘制圆弧
// arc(x, y, radius, startAngle, endAngle, anticlockwise)
// 完整圆形
function drawCircle(x, y, radius, fillColor, strokeColor) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
if (fillColor) {
ctx.fillStyle = fillColor;
ctx.fill();
}
if (strokeColor) {
ctx.strokeStyle = strokeColor;
ctx.stroke();
}
}
// 绘制不同样式的圆形
drawCircle(100, 100, 40, 'red', null); // 填充圆
drawCircle(250, 100, 40, null, 'blue'); // 描边圆
drawCircle(400, 100, 40, 'green', 'purple'); // 填充+描边圆
2.2 弧形与扇形
// 绘制弧形
function drawArc() {
ctx.beginPath();
ctx.arc(150, 250, 50, 0, Math.PI); // 半圆
ctx.strokeStyle = 'orange';
ctx.lineWidth = 3;
ctx.stroke();
}
// 绘制扇形
function drawSector() {
ctx.beginPath();
ctx.moveTo(300, 250); // 移动到圆心
ctx.arc(300, 250, 50, 0, Math.PI / 2); // 四分之一圆
ctx.closePath();
ctx.fillStyle = 'lightblue';
ctx.fill();
ctx.strokeStyle = 'darkblue';
ctx.stroke();
}
// 绘制圆环
function drawRing() {
// 外圆
ctx.beginPath();
ctx.arc(450, 250, 50, 0, Math.PI * 2);
ctx.fillStyle = 'yellow';
ctx.fill();
// 内圆(使用globalCompositeOperation)
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
ctx.arc(450, 250, 30, 0, Math.PI * 2);
ctx.fill();
ctx.globalCompositeOperation = 'source-over'; // 恢复默认
}
drawArc();
drawSector();
drawRing();
2.3 椭圆绘制
// ellipse() 方法绘制椭圆
// ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
function drawEllipse() {
ctx.beginPath();
ctx.ellipse(200, 400, 80, 40, 0, 0, Math.PI * 2);
ctx.fillStyle = 'lightgreen';
ctx.fill();
ctx.strokeStyle = 'darkgreen';
ctx.stroke();
}
// 旋转的椭圆
function drawRotatedEllipse() {
ctx.beginPath();
ctx.ellipse(400, 400, 80, 40, Math.PI / 4, 0, Math.PI * 2);
ctx.fillStyle = 'lightcoral';
ctx.fill();
}
drawEllipse();
drawRotatedEllipse();
3. 线条绘制
3.1 基础线条
// 基础直线
function drawBasicLines() {
// 简单直线
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(200, 100);
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
// 连续线条
ctx.beginPath();
ctx.moveTo(50, 120);
ctx.lineTo(100, 150);
ctx.lineTo(150, 120);
ctx.lineTo(200, 150);
ctx.strokeStyle = 'red';
ctx.lineWidth = 3;
ctx.stroke();
}
drawBasicLines();
3.2 线条样式
// 不同线条样式
function drawLineStyles() {
const styles = [
{ cap: 'butt', join: 'miter', dash: [] },
{ cap: 'round', join: 'round', dash: [] },
{ cap: 'square', join: 'bevel', dash: [] },
{ cap: 'round', join: 'round', dash: [5, 5] },
{ cap: 'round', join: 'round', dash: [10, 5, 5, 5] }
];
styles.forEach((style, index) => {
const y = 200 + index * 40;
ctx.beginPath();
ctx.moveTo(50, y);
ctx.lineTo(150, y);
ctx.lineTo(200, y - 20);
ctx.lineTo(250, y);
ctx.lineCap = style.cap;
ctx.lineJoin = style.join;
ctx.setLineDash(style.dash);
ctx.lineWidth = 8;
ctx.strokeStyle = `hsl(${index * 60}, 70%, 50%)`;
ctx.stroke();
});
// 重置虚线
ctx.setLineDash([]);
}
drawLineStyles();
3.3 曲线绘制
// 二次贝塞尔曲线
function drawQuadraticCurve() {
ctx.beginPath();
ctx.moveTo(300, 200);
ctx.quadraticCurveTo(350, 150, 400, 200); // 控制点, 结束点
ctx.strokeStyle = 'blue';
ctx.lineWidth = 3;
ctx.stroke();
// 绘制控制点(辅助线)
ctx.beginPath();
ctx.moveTo(300, 200);
ctx.lineTo(350, 150);
ctx.lineTo(400, 200);
ctx.strokeStyle = 'rgba(255, 0, 0, 0.3)';
ctx.lineWidth = 1;
ctx.stroke();
// 标记控制点
ctx.fillStyle = 'red';
ctx.fillRect(348, 148, 4, 4);
}
// 三次贝塞尔曲线
function drawCubicCurve() {
ctx.beginPath();
ctx.moveTo(300, 280);
ctx.bezierCurveTo(320, 230, 380, 230, 400, 280); // 控制点1, 控制点2, 结束点
ctx.strokeStyle = 'green';
ctx.lineWidth = 3;
ctx.stroke();
// 绘制控制点(辅助线)
ctx.beginPath();
ctx.moveTo(300, 280);
ctx.lineTo(320, 230);
ctx.moveTo(400, 280);
ctx.lineTo(380, 230);
ctx.strokeStyle = 'rgba(255, 0, 0, 0.3)';
ctx.lineWidth = 1;
ctx.stroke();
// 标记控制点
ctx.fillStyle = 'red';
ctx.fillRect(318, 228, 4, 4);
ctx.fillRect(378, 228, 4, 4);
}
drawQuadraticCurve();
drawCubicCurve();
4. 路径系统
4.1 路径基础
// 路径的基本概念
function pathBasics() {
// 开始新路径
ctx.beginPath();
// 移动画笔(不绘制)
ctx.moveTo(50, 350);
// 绘制线条
ctx.lineTo(100, 350);
ctx.lineTo(75, 320);
// 闭合路径
ctx.closePath();
// 填充和描边
ctx.fillStyle = 'yellow';
ctx.fill();
ctx.strokeStyle = 'orange';
ctx.lineWidth = 2;
ctx.stroke();
}
pathBasics();
4.2 复杂路径
// 组合路径绘制星形
function drawStar(x, y, outerRadius, innerRadius, points) {
ctx.beginPath();
for (let i = 0; i < points * 2; i++) {
const angle = (i * Math.PI) / points;
const radius = i % 2 === 0 ? outerRadius : innerRadius;
const px = x + Math.cos(angle) * radius;
const py = y + Math.sin(angle) * radius;
if (i === 0) {
ctx.moveTo(px, py);
} else {
ctx.lineTo(px, py);
}
}
ctx.closePath();
ctx.fillStyle = 'gold';
ctx.fill();
ctx.strokeStyle = 'darkorange';
ctx.lineWidth = 2;
ctx.stroke();
}
// 绘制多边形
function drawPolygon(x, y, radius, sides) {
ctx.beginPath();
for (let i = 0; i < sides; i++) {
const angle = (i * 2 * Math.PI) / sides;
const px = x + Math.cos(angle) * radius;
const py = y + Math.sin(angle) * radius;
if (i === 0) {
ctx.moveTo(px, py);
} else {
ctx.lineTo(px, py);
}
}
ctx.closePath();
ctx.fillStyle = 'lightblue';
ctx.fill();
ctx.strokeStyle = 'darkblue';
ctx.stroke();
}
// 绘制示例
drawStar(200, 380, 30, 15, 5); // 五角星
drawPolygon(300, 380, 25, 6); // 六边形
drawPolygon(400, 380, 25, 8); // 八边形
4.3 路径检测
// 点击检测
function setupPathDetection() {
// 创建一个圆形路径
const circleX = 500;
const circleY = 380;
const circleRadius = 30;
ctx.beginPath();
ctx.arc(circleX, circleY, circleRadius, 0, Math.PI * 2);
ctx.fillStyle = 'lightgreen';
ctx.fill();
// 添加点击事件
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 重新创建路径进行检测
ctx.beginPath();
ctx.arc(circleX, circleY, circleRadius, 0, Math.PI * 2);
// 检测点是否在路径内
if (ctx.isPointInPath(x, y)) {
console.log('点击了圆形!');
// 改变颜色
ctx.fillStyle = 'red';
ctx.fill();
}
});
}
setupPathDetection();
5. 图案与纹理
5.1 图案填充
// 创建图案
function createPatterns() {
// 创建一个小的canvas作为图案
const patternCanvas = document.createElement('canvas');
patternCanvas.width = 20;
patternCanvas.height = 20;
const pCtx = patternCanvas.getContext('2d');
// 绘制图案
pCtx.fillStyle = '#ff6b6b';
pCtx.fillRect(0, 0, 10, 10);
pCtx.fillStyle = '#4ecdc4';
pCtx.fillRect(10, 10, 10, 10);
// 创建图案
const pattern = ctx.createPattern(patternCanvas, 'repeat');
// 使用图案填充
ctx.fillStyle = pattern;
ctx.fillRect(50, 450, 150, 100);
}
createPatterns();
5.2 图像图案
// 使用图像创建图案
function createImagePattern() {
const img = new Image();
img.onload = function() {
const pattern = ctx.createPattern(img, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(250, 450, 150, 100);
};
// 这里需要一个实际的图像URL
// img.src = 'pattern.png';
}
// createImagePattern();
6. 阴影效果
6.1 基础阴影
// 阴影设置
function drawWithShadow() {
// 设置阴影属性
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
// 绘制带阴影的矩形
ctx.fillStyle = 'blue';
ctx.fillRect(450, 450, 100, 80);
// 清除阴影设置
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
drawWithShadow();
7. 实用绘图函数
7.1 绘图工具函数
// 绘图工具类
class DrawingUtils {
constructor(ctx) {
this.ctx = ctx;
}
// 绘制圆角矩形
roundRect(x, y, width, height, radius) {
this.ctx.beginPath();
this.ctx.moveTo(x + radius, y);
this.ctx.lineTo(x + width - radius, y);
this.ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
this.ctx.lineTo(x + width, y + height - radius);
this.ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
this.ctx.lineTo(x + radius, y + height);
this.ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
this.ctx.lineTo(x, y + radius);
this.ctx.quadraticCurveTo(x, y, x + radius, y);
this.ctx.closePath();
}
// 绘制箭头
arrow(fromX, fromY, toX, toY, headSize = 10) {
const angle = Math.atan2(toY - fromY, toX - fromX);
// 绘制线条
this.ctx.beginPath();
this.ctx.moveTo(fromX, fromY);
this.ctx.lineTo(toX, toY);
this.ctx.stroke();
// 绘制箭头头部
this.ctx.beginPath();
this.ctx.moveTo(toX, toY);
this.ctx.lineTo(
toX - headSize * Math.cos(angle - Math.PI / 6),
toY - headSize * Math.sin(angle - Math.PI / 6)
);
this.ctx.moveTo(toX, toY);
this.ctx.lineTo(
toX - headSize * Math.cos(angle + Math.PI / 6),
toY - headSize * Math.sin(angle + Math.PI / 6)
);
this.ctx.stroke();
}
// 绘制网格
grid(step = 50, color = '#e0e0e0') {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = 1;
// 垂直线
for (let x = 0; x <= this.ctx.canvas.width; x += step) {
this.ctx.beginPath();
this.ctx.moveTo(x, 0);
this.ctx.lineTo(x, this.ctx.canvas.height);
this.ctx.stroke();
}
// 水平线
for (let y = 0; y <= this.ctx.canvas.height; y += step) {
this.ctx.beginPath();
this.ctx.moveTo(0, y);
this.ctx.lineTo(this.ctx.canvas.width, y);
this.ctx.stroke();
}
}
}
// 使用示例
const utils = new DrawingUtils(ctx);
// 绘制圆角矩形
utils.roundRect(600, 50, 120, 80, 10);
ctx.fillStyle = 'lightcoral';
ctx.fill();
ctx.strokeStyle = 'darkred';
ctx.stroke();
// 绘制箭头
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
utils.arrow(600, 200, 720, 250);
// 绘制网格(可选,用于调试)
// utils.grid(50, 'rgba(0, 0, 0, 0.1)');
8. 性能优化技巧
8.1 批量绘制
// 低效的绘制方式
function inefficientDrawing() {
for (let i = 0; i < 1000; i++) {
ctx.fillStyle = `hsl(${i % 360}, 50%, 50%)`;
ctx.fillRect(Math.random() * 800, Math.random() * 600, 5, 5);
}
}
// 高效的绘制方式
function efficientDrawing() {
// 批量设置相同样式
const colors = ['red', 'green', 'blue', 'yellow', 'purple'];
colors.forEach(color => {
ctx.fillStyle = color;
ctx.beginPath();
// 批量绘制相同颜色的图形
for (let i = 0; i < 200; i++) {
const x = Math.random() * 800;
const y = Math.random() * 600;
ctx.rect(x, y, 5, 5);
}
ctx.fill();
});
}
8.2 离屏渲染
// 离屏Canvas优化
function offscreenOptimization() {
// 创建离屏canvas
const offscreen = document.createElement('canvas');
offscreen.width = 200;
offscreen.height = 200;
const offCtx = offscreen.getContext('2d');
// 在离屏canvas上绘制复杂图形
offCtx.fillStyle = 'blue';
offCtx.fillRect(0, 0, 200, 200);
for (let i = 0; i < 50; i++) {
offCtx.fillStyle = `hsl(${i * 7}, 70%, 50%)`;
offCtx.beginPath();
offCtx.arc(
Math.random() * 200,
Math.random() * 200,
Math.random() * 10 + 5,
0,
Math.PI * 2
);
offCtx.fill();
}
// 将离屏canvas绘制到主canvas(只需要一次drawImage调用)
ctx.drawImage(offscreen, 600, 300);
}
offscreenOptimization();
9. 小结
本章详细介绍了Canvas的基本绘图API:
- 矩形绘制:fillRect、strokeRect、clearRect方法及样式设置
- 圆形绘制:arc方法绘制圆形、弧形、扇形和椭圆
- 线条绘制:直线、曲线、贝塞尔曲线及线条样式
- 路径系统:路径的创建、组合和检测
- 图案纹理:使用图案和图像进行填充
- 阴影效果:为图形添加阴影
- 实用函数:常用的绘图工具函数
- 性能优化:批量绘制和离屏渲染技巧
下一章我们将学习更复杂的路径绘制技术和高级图形效果。
10. 练习题
- 绘制一个彩虹色的同心圆
- 创建一个时钟表盘(包含刻度和数字)
- 绘制一个心形图案
- 实现一个简单的绘图板功能
- 创建一个动态的波浪线效果