Canvas是HTML5新增的绘图元素,通过JavaScript可以在Canvas上绘制图形、动画、游戏等。Canvas提供了一个2D渲染上下文,可以绘制各种形状、图像和文本。
东巴文(db-w.cn) 认为:Canvas为Web开发打开了图形编程的大门,让浏览器成为强大的图形平台。
<canvas id="myCanvas" width="400" height="300">
您的浏览器不支持canvas标签。
</canvas>
| 属性 | 说明 | 默认值 |
|---|---|---|
width |
画布宽度 | 300 |
height |
画布高度 | 150 |
id |
唯一标识符 | - |
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
东巴文点评:Canvas本身没有绘图能力,必须通过JavaScript获取渲染上下文才能绘图。
Canvas使用二维坐标系统,原点(0,0)在左上角。
(0,0) ────────► X轴
│
│
│
▼
Y轴
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 在坐标(10, 10)绘制一个点
ctx.fillRect(10, 10, 1, 1);
// 在坐标(100, 50)绘制一个矩形
ctx.fillRect(100, 50, 80, 60);
Canvas提供了三种绘制矩形的方法。
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 设置填充颜色
ctx.fillStyle = 'red';
// 绘制填充矩形
ctx.fillRect(10, 10, 100, 50);
// 设置描边颜色
ctx.strokeStyle = 'blue';
// 设置线宽
ctx.lineWidth = 3;
// 绘制描边矩形
ctx.strokeRect(120, 10, 100, 50);
// 清除矩形区域
ctx.clearRect(50, 20, 30, 30);
| 方法 | 说明 |
|---|---|
fillRect(x, y, width, height) |
填充矩形 |
strokeRect(x, y, width, height) |
描边矩形 |
clearRect(x, y, width, height) |
清除矩形 |
东巴文点评:矩形是Canvas中唯一可以直接绘制的形状,其他形状需要使用路径。
路径是Canvas绘图的核心概念,可以绘制各种复杂形状。
beginPath() - 开始路径closePath() - 闭合路径(可选)fill() 或 stroke() - 填充或描边const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 开始路径
ctx.beginPath();
// 移动到起点
ctx.moveTo(50, 50);
// 连线到其他点
ctx.lineTo(150, 50);
ctx.lineTo(100, 150);
// 闭合路径
ctx.closePath();
// 填充
ctx.fillStyle = 'green';
ctx.fill();
// 描边
ctx.strokeStyle = 'darkgreen';
ctx.lineWidth = 2;
ctx.stroke();
| 方法 | 说明 |
|---|---|
beginPath() |
开始新路径 |
closePath() |
闭合路径 |
moveTo(x, y) |
移动到点 |
lineTo(x, y) |
连线到点 |
fill() |
填充路径 |
stroke() |
描边路径 |
ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise);
参数说明:
| 参数 | 说明 |
|---|---|
x |
圆心X坐标 |
y |
圆心Y坐标 |
radius |
半径 |
startAngle |
起始角度(弧度) |
endAngle |
结束角度(弧度) |
counterclockwise |
是否逆时针(默认false) |
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 开始路径
ctx.beginPath();
// 绘制圆形
ctx.arc(100, 100, 50, 0, Math.PI * 2);
// 填充
ctx.fillStyle = 'blue';
ctx.fill();
ctx.beginPath();
ctx.arc(200, 100, 50, 0, Math.PI);
ctx.fillStyle = 'orange';
ctx.fill();
ctx.beginPath();
ctx.moveTo(100, 200); // 移动到圆心
ctx.arc(100, 200, 50, 0, Math.PI / 2); // 绘制圆弧
ctx.closePath(); // 闭合路径
ctx.fillStyle = 'purple';
ctx.fill();
东巴文点评:角度使用弧度制,0度在3点钟方向,顺时针旋转。
Canvas支持二次和三次贝塞尔曲线。
ctx.quadraticCurveTo(cpx, cpy, x, y);
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.quadraticCurveTo(150, 50, 250, 200);
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
ctx.stroke();
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.bezierCurveTo(100, 50, 200, 350, 250, 200);
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;
ctx.stroke();
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 设置字体
ctx.font = '30px Arial';
// 设置填充颜色
ctx.fillStyle = 'black';
// 绘制填充文本
ctx.fillText('东巴文Canvas', 50, 50);
ctx.font = '40px Arial';
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;
ctx.strokeText('东巴文Canvas', 50, 100);
| 属性/方法 | 说明 |
|---|---|
font |
字体样式 |
textAlign |
水平对齐(start/end/left/right/center) |
textBaseline |
垂直对齐(top/middle/bottom等) |
fillText(text, x, y) |
填充文本 |
strokeText(text, x, y) |
描边文本 |
measureText(text) |
测量文本宽度 |
ctx.font = '20px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('居中文本', canvas.width / 2, canvas.height / 2);
东巴文点评:Canvas文本渲染能力强大,但不如HTML文本灵活,适合图表、游戏等场景。
ctx.drawImage(image, x, y);
ctx.drawImage(image, x, y, width, height);
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
};
img.src = 'image.jpg';
img.onload = function() {
ctx.drawImage(img, 0, 0, 200, 150);
};
img.onload = function() {
// 裁剪源图像的(50,50,100,100)区域,绘制到画布的(0,0,200,200)
ctx.drawImage(img, 50, 50, 100, 100, 0, 0, 200, 200);
};
// 颜色名称
ctx.fillStyle = 'red';
// 十六进制
ctx.fillStyle = '#ff0000';
// RGB
ctx.fillStyle = 'rgb(255, 0, 0)';
// RGBA
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
ctx.strokeStyle = 'blue';
ctx.strokeStyle = '#0000ff';
ctx.strokeStyle = 'rgb(0, 0, 255)';
ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)';
ctx.globalAlpha = 0.5; // 设置全局透明度
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 创建线性渐变
const 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(10, 10, 200, 100);
// 创建径向渐变
const gradient = ctx.createRadialGradient(100, 100, 0, 100, 100, 100);
// 添加颜色停止点
gradient.addColorStop(0, 'red');
gradient.addColorStop(0.5, 'yellow');
gradient.addColorStop(1, 'blue');
// 使用渐变填充
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(100, 100, 100, 0, Math.PI * 2);
ctx.fill();
东巴文点评:渐变可以为Canvas图形添加丰富的视觉效果。
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = function() {
// 创建图案
const pattern = ctx.createPattern(img, 'repeat');
// 使用图案填充
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 400, 300);
};
img.src = 'pattern.png';
| 值 | 说明 |
|---|---|
repeat |
水平和垂直重复 |
repeat-x |
水平重复 |
repeat-y |
垂直重复 |
no-repeat |
不重复 |
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 设置阴影
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
// 绘制带阴影的矩形
ctx.fillStyle = 'blue';
ctx.fillRect(50, 50, 100, 80);
| 属性 | 说明 |
|---|---|
shadowColor |
阴影颜色 |
shadowBlur |
模糊程度 |
shadowOffsetX |
X轴偏移 |
shadowOffsetY |
Y轴偏移 |
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 50, 50);
// 平移
ctx.translate(100, 100);
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 50, 50);
ctx.translate(100, 100); // 移动到旋转中心
ctx.rotate(Math.PI / 4); // 旋转45度
ctx.fillStyle = 'green';
ctx.fillRect(-25, -25, 50, 50);
ctx.scale(2, 2); // 放大2倍
ctx.fillStyle = 'yellow';
ctx.fillRect(10, 10, 50, 50);
| 方法 | 说明 |
|---|---|
translate(x, y) |
平移 |
rotate(angle) |
旋转(弧度) |
scale(x, y) |
缩放 |
transform(a, b, c, d, e, f) |
矩阵变换 |
setTransform(a, b, c, d, e, f) |
设置变换矩阵 |
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 保存状态
ctx.save();
// 设置样式并绘制
ctx.fillStyle = 'red';
ctx.translate(100, 100);
ctx.fillRect(0, 0, 50, 50);
// 恢复状态
ctx.restore();
// 使用之前的样式绘制
ctx.fillRect(0, 0, 50, 50); // 不会被平移
东巴文点评:save()和restore()是Canvas状态管理的核心,可以保存和恢复样式、变换等状态。
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
let x = 0;
function animate() {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制矩形
ctx.fillStyle = 'blue';
ctx.fillRect(x, 100, 50, 50);
// 更新位置
x += 2;
// 循环动画
requestAnimationFrame(animate);
}
animate();
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const ball = {
x: 50,
y: 50,
vx: 4,
vy: 4,
radius: 20,
color: 'blue'
};
function drawBall() {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.closePath();
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
// 更新位置
ball.x += ball.vx;
ball.y += ball.vy;
// 边界检测
if (ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
ball.vx = -ball.vx;
}
if (ball.y + ball.radius > canvas.height || ball.y - ball.radius < 0) {
ball.vy = -ball.vy;
}
requestAnimationFrame(animate);
}
animate();
东巴文点评:requestAnimationFrame是实现Canvas动画的最佳方式,它会在浏览器重绘之前调用指定的回调函数。
<!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>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
canvas {
border: 1px solid #ccc;
display: block;
margin: 20px 0;
}
.controls {
margin: 20px 0;
}
button {
padding: 10px 20px;
margin: 5px;
border: none;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 5px;
cursor: pointer;
}
button:hover {
opacity: 0.9;
}
</style>
</head>
<body>
<h1>Canvas绘图示例</h1>
<canvas id="myCanvas" width="600" height="400"></canvas>
<div class="controls">
<button onclick="drawRectangles()">绘制矩形</button>
<button onclick="drawCircles()">绘制圆形</button>
<button onclick="drawText()">绘制文本</button>
<button onclick="drawGradient()">绘制渐变</button>
<button onclick="animate()">动画演示</button>
<button onclick="clearCanvas()">清除画布</button>
</div>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 绘制矩形
function drawRectangles() {
clearCanvas();
ctx.fillStyle = 'red';
ctx.fillRect(50, 50, 100, 80);
ctx.strokeStyle = 'blue';
ctx.lineWidth = 3;
ctx.strokeRect(200, 50, 100, 80);
const gradient = ctx.createLinearGradient(350, 50, 450, 130);
gradient.addColorStop(0, 'green');
gradient.addColorStop(1, 'yellow');
ctx.fillStyle = gradient;
ctx.fillRect(350, 50, 100, 80);
}
// 绘制圆形
function drawCircles() {
clearCanvas();
ctx.beginPath();
ctx.arc(100, 200, 50, 0, Math.PI * 2);
ctx.fillStyle = 'purple';
ctx.fill();
ctx.beginPath();
ctx.arc(250, 200, 50, 0, Math.PI);
ctx.fillStyle = 'orange';
ctx.fill();
ctx.beginPath();
ctx.moveTo(400, 200);
ctx.arc(400, 200, 50, 0, Math.PI / 2);
ctx.closePath();
ctx.fillStyle = 'cyan';
ctx.fill();
}
// 绘制文本
function drawText() {
clearCanvas();
ctx.font = 'bold 40px Arial';
ctx.fillStyle = 'blue';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('东巴文Canvas', canvas.width / 2, canvas.height / 2);
ctx.font = '20px Arial';
ctx.strokeStyle = 'red';
ctx.lineWidth = 1;
ctx.strokeText('Canvas绘图教程', canvas.width / 2, canvas.height / 2 + 50);
}
// 绘制渐变
function drawGradient() {
clearCanvas();
// 线性渐变
const linearGradient = ctx.createLinearGradient(50, 50, 250, 50);
linearGradient.addColorStop(0, 'red');
linearGradient.addColorStop(0.5, 'yellow');
linearGradient.addColorStop(1, 'blue');
ctx.fillStyle = linearGradient;
ctx.fillRect(50, 50, 200, 100);
// 径向渐变
const radialGradient = ctx.createRadialGradient(450, 100, 0, 450, 100, 80);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(0.5, 'yellow');
radialGradient.addColorStop(1, 'red');
ctx.fillStyle = radialGradient;
ctx.beginPath();
ctx.arc(450, 100, 80, 0, Math.PI * 2);
ctx.fill();
}
// 动画演示
let animationId;
let x = 0;
function animate() {
if (animationId) {
cancelAnimationFrame(animationId);
}
x = 0;
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'blue';
ctx.fillRect(x, canvas.height / 2 - 25, 50, 50);
x += 3;
if (x > canvas.width) {
x = -50;
}
animationId = requestAnimationFrame(loop);
}
loop();
}
// 清除画布
function clearCanvas() {
if (animationId) {
cancelAnimationFrame(animationId);
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
</script>
</body>
</html>
| 方法 | 说明 |
|---|---|
fillRect(x, y, w, h) |
填充矩形 |
strokeRect(x, y, w, h) |
描边矩形 |
clearRect(x, y, w, h) |
清除矩形 |
beginPath() |
开始路径 |
closePath() |
闭合路径 |
moveTo(x, y) |
移动到点 |
lineTo(x, y) |
连线到点 |
arc(x, y, r, start, end) |
绘制圆弧 |
fill() |
填充 |
stroke() |
描边 |
| 属性 | 说明 |
|---|---|
fillStyle |
填充样式 |
strokeStyle |
描边样式 |
lineWidth |
线宽 |
lineCap |
线端样式 |
lineJoin |
线连接样式 |
globalAlpha |
全局透明度 |
| 方法 | 说明 |
|---|---|
translate(x, y) |
平移 |
rotate(angle) |
旋转 |
scale(x, y) |
缩放 |
save() |
保存状态 |
restore() |
恢复状态 |
// 推荐:避免在动画中频繁创建对象
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 100);
requestAnimationFrame(animate);
}
// 不推荐:在动画中创建渐变
function animate() {
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 100);
requestAnimationFrame(animate);
}
function resizeCanvas() {
const canvas = document.getElementById('myCanvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
canvas.width = 400 * dpr;
canvas.height = 300 * dpr;
canvas.style.width = '400px';
canvas.style.height = '300px';
ctx.scale(dpr, dpr);
问题1:Canvas中用于开始新路径的方法是?
A. startPath()
B. beginPath()
C. newPath()
D. createPath()
答案:B
东巴文解释:beginPath()方法用于开始一个新的路径,这是Canvas绘图的标准方法。
问题2:以下哪个方法用于绘制圆形?
A. drawCircle()
B. circle()
C. arc()
D. ellipse()
答案:C
东巴文解释:Canvas使用arc()方法绘制圆形和圆弧,通过设置起始角度为0,结束角度为Math.PI * 2可以绘制完整的圆。
任务:创建一个Canvas动画,实现一个在画布中弹跳的小球。
<details> <summary>点击查看参考答案</summary><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>弹跳球动画</title>
<style>
canvas {
border: 1px solid #ccc;
display: block;
margin: 20px auto;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="600" height="400"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const ball = {
x: 100,
y: 100,
vx: 5,
vy: 3,
radius: 20,
color: 'blue'
};
function drawBall() {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.closePath();
}
function update() {
ball.x += ball.vx;
ball.y += ball.vy;
if (ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
ball.vx = -ball.vx;
}
if (ball.y + ball.radius > canvas.height || ball.y - ball.radius < 0) {
ball.vy = -ball.vy;
}
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
update();
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>
</details>
东巴文(db-w.cn) - 让编程学习更有趣、更高效!