文章目录
HTML5 Canvas从空白画布到交互游戏
HTML5 Canvas 是浏览器原生的绘图API,可实现高性能2D图形绘制、动画、交互游戏甚至数据可视化。从零基础到独立开发项目,需经历「基础认知→核心绘图→交互动画→高级特性→项目实战」五个阶段,每个阶段都有明确的知识点和实践目标。
一、阶段1:零基础入门——认识Canvas的"画布本质"
目标:掌握Canvas的基本概念和绘图环境搭建,能绘制简单图形。
核心知识点
-
Canvas基础概念
<canvas>标签是绘图容器,本身不绘制内容,需通过JavaScript的CanvasRenderingContext2D上下文对象操作。- 宽高设置:必须通过
width和height属性(而非CSS)定义,否则会导致图形拉伸。
<!-- 正确设置宽高:300x200像素的画布 --> <canvas id="myCanvas" width="600" height="400"></canvas> <style> #myCanvas { border: 1px solid #000; } /* 仅用于显示边框,不影响绘图尺寸 */ </style> -
获取绘图上下文
通过getContext('2d')获取2D绘图上下文(核心操作对象),所有绘图API均通过该对象调用。const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); // 2D绘图上下文,后续所有操作通过ctx执行 -
基础图形绘制
- 线段:
beginPath()开始路径→moveTo(x,y)起点→lineTo(x,y)终点→stroke()描边。 - 矩形:
fillRect(x,y,w,h)填充矩形;strokeRect(x,y,w,h)描边矩形;clearRect(x,y,w,h)清除矩形区域。 - 圆形:
arc(x,y,r,startAngle,endAngle,anticlockwise)绘制圆弧(圆形是360°的圆弧)。
- 线段:
代码示例:绘制基础图形
// 1. 绘制线段
ctx.beginPath();
ctx.moveTo(50, 50); // 起点(50,50)
ctx.lineTo(200, 50); // 终点(200,50)
ctx.strokeStyle = 'red'; // 线段颜色
ctx.lineWidth = 3; // 线段宽度
ctx.stroke(); // 描边
// 2. 绘制填充矩形
ctx.fillStyle = 'blue'; // 填充颜色
ctx.fillRect(50, 100, 150, 80); // x=50,y=100,宽150,高80
// 3. 绘制圆形
ctx.beginPath();
ctx.arc(350, 140, 40, 0, Math.PI * 2, false); // 圆心(350,140),半径40,0~360°
ctx.fillStyle = 'green';
ctx.fill(); // 填充
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke(); // 描边
注意事项
- 宽高必须通过标签属性设置(如
width="600"),用CSS设置会导致图形按比例拉伸(例如CSS设width:300px会将600px的画布压缩为300px,图形变模糊)。 - 每次绘制新图形前需用
beginPath()开启新路径,否则会与上一路径关联(导致意外描边)。
二、阶段2:进阶绘图——掌握样式与复杂图形
目标:学习颜色、渐变、文本、图像等高级绘图技巧,绘制更丰富的视觉元素。
核心知识点
-
颜色与样式
- 填充与描边:
fillStyle(填充颜色)和strokeStyle(描边颜色)支持颜色值(#ff0000)、rgba(rgba(255,0,0,0.5))、渐变或图案。 - 线性渐变:
createLinearGradient(x1,y1,x2,y2)创建渐变对象→addColorStop(offset, color)添加色标→赋值给fillStyle。 - 径向渐变:
createRadialGradient(x1,y1,r1,x2,y2,r2)(从圆心1到圆心2的渐变)。 - 图案填充:
createPattern(image, repeat)(用图片或Canvas作为填充图案,repeat可选repeat/no-repeat等)。
// 线性渐变示例 const gradient = ctx.createLinearGradient(50, 50, 250, 50); // 从(50,50)到(250,50)的水平渐变 gradient.addColorStop(0, 'red'); // 起点红色 gradient.addColorStop(1, 'blue'); // 终点蓝色 ctx.fillStyle = gradient; ctx.fillRect(50, 50, 200, 100); // 用渐变填充矩形 - 填充与描边:
-
文本绘制
- 填充文本:
fillText(text, x, y, maxWidth)(maxWidth可选,限制文本最大宽度)。 - 描边文本:
strokeText(text, x, y)。 - 字体设置:
font(如"20px Arial")、textAlign(水平对齐:left/center/right)、textBaseline(垂直对齐:top/middle/bottom)。
ctx.font = 'bold 30px "Microsoft YaHei"'; ctx.fillStyle = '#333'; ctx.textAlign = 'center'; // 水平居中 ctx.textBaseline = 'middle'; // 垂直居中 ctx.fillText('Canvas文本', 300, 100); // 在(300,100)居中绘制文本 - 填充文本:
-
图像操作
- 绘制图片:
drawImage(image, x, y)(基础用法);drawImage(image, x, y, w, h)(缩放绘制);drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)(裁剪原图区域绘制)。 - 注意:图片必须加载完成后才能绘制(监听
load事件)。
const img = new Image(); img.src = 'image.jpg'; // 图片路径 img.onload = () => { // 绘制完整图片到(50,50) ctx.drawImage(img, 50, 50); // 裁剪原图(0,0)开始的200x200区域,绘制到(300,50),缩放为150x150 ctx.drawImage(img, 0, 0, 200, 200, 300, 50, 150, 150); }; - 绘制图片:
最佳实践
- 渐变和图案对象可复用(避免重复创建,提升性能)。
- 文本绘制时尽量指定
maxWidth,防止文本溢出容器。 - 图像绘制前务必检查
onload事件(未加载完的图片无法绘制,不会报错但无效果)。
三、阶段3:交互与动画——让画布"动起来"
目标:实现鼠标/触摸交互,掌握Canvas动画原理,开发动态效果。
核心知识点
-
交互事件处理
- 鼠标坐标转换:Canvas的
getBoundingClientRect()获取元素在视口的位置,计算鼠标在Canvas中的相对坐标(mouseX = event.clientX - rect.left,mouseY = event.clientY - rect.top)。 - 常用事件:
mousedown(鼠标按下)、mousemove(鼠标移动)、mouseup(鼠标释放)、touchstart/touchmove/touchend(触摸事件)。
// 鼠标在Canvas中的坐标计算 canvas.addEventListener('mousemove', (e) => { const rect = canvas.getBoundingClientRect(); // 获取Canvas的位置和尺寸 const x = e.clientX - rect.left; // 相对Canvas的X坐标 const y = e.clientY - rect.top; // 相对Canvas的Y坐标 console.log(`鼠标位置:(${x}, ${y})`); }); - 鼠标坐标转换:Canvas的
-
动画原理与实现
- 核心逻辑:通过
requestAnimationFrame(浏览器优化的动画API)周期性清除画布并重新绘制,形成动画效果。 - 步骤:初始化状态→清除画布(
clearRect或fillRect覆盖)→更新状态(如位置、角度)→绘制新帧→循环。
// 小球动画示例 let x = 50; // 小球X坐标 const speed = 2; // 移动速度 function animate() { // 1. 清除画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 2. 更新状态(边界检测) x += speed; if (x > canvas.width - 30 || x < 30) { // 小球半径30,边界反弹 speed = -speed; } // 3. 绘制新帧 ctx.beginPath(); ctx.arc(x, 100, 30, 0, Math.PI * 2); ctx.fillStyle = 'orange'; ctx.fill(); // 4. 循环调用 requestAnimationFrame(animate); } animate(); // 启动动画 - 核心逻辑:通过
-
碰撞检测基础
- 矩形碰撞:两个矩形的
x/y/宽/高交叉判断(rect1.x < rect2.x + rect2.w && ...)。 - 圆形碰撞:两圆心距离小于半径之和(
Math.hypot(x1-x2, y1-y2) < r1 + r2)。
- 矩形碰撞:两个矩形的
注意事项
- 动画中必须先清除画布(否则会保留上一帧),
clearRect(0,0,width,height)是最常用方式。 - 避免在动画中使用
setInterval(时间精度低,可能导致卡顿),优先用requestAnimationFrame(与浏览器刷新同步,性能更好)。 - 触摸事件需处理
touches列表(e.touches[0]获取第一个触摸点),并注意防止默认行为(如页面滚动)。
四、阶段4:高级特性——性能优化与特殊效果
目标:掌握像素操作、合成模式、离屏渲染等高级技巧,优化复杂场景性能。
核心知识点
-
像素级操作
getImageData(x,y,w,h):获取指定区域的像素数据(ImageData对象,包含data数组,每4个元素代表一个像素的r,g,b,a)。putImageData(imagedata, x,y):将像素数据绘制到画布。- 应用:滤镜效果(反色、灰度)、像素识别。
// 反色效果示例 function invertColors() { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { data[i] = 255 - data[i]; // 红色反色 data[i + 1] = 255 - data[i + 1]; // 绿色反色 data[i + 2] = 255 - data[i + 2]; // 蓝色反色 // alpha通道不变(data[i+3]) } ctx.putImageData(imageData, 0, 0); // 绘制处理后的像素 } -
合成与裁剪
- 合成模式:
globalCompositeOperation控制新绘制图形与已有图形的混合方式(如"source-over"默认覆盖,"destination-over"在下方绘制,"lighter"叠加增亮)。 - 裁剪路径:
clip()将后续绘制限制在当前路径内(需配合beginPath()和图形路径)。
// 合成模式示例:圆形内绘制图片,圆形外透明 ctx.beginPath(); ctx.arc(200, 200, 100, 0, Math.PI * 2); ctx.clip(); // 裁剪为圆形区域 const img = new Image(); img.src = 'photo.jpg'; img.onload = () => { ctx.drawImage(img, 100, 100, 200, 200); // 仅圆形区域显示图片 }; - 合成模式:
-
性能优化技巧
- 离屏Canvas:将静态内容绘制到隐藏的
canvas(离屏画布),需要时通过drawImage绘制到主画布(减少重复绘制)。 - 减少绘制区域:仅重绘变化的部分(而非全画布清除)。
- 避免频繁修改
fillStyle/strokeStyle(状态切换耗时)。
- 离屏Canvas:将静态内容绘制到隐藏的
最佳实践
- 像素操作(
getImageData/putImageData)是性能密集型操作,避免在动画中频繁调用(可配合离屏Canvas预处理)。 - 合成模式和裁剪路径会改变绘图状态,建议用
ctx.save()保存状态、ctx.restore()恢复(避免影响后续绘制)。
五、阶段5:项目开发——从需求到上线
目标:整合所学知识,独立开发完整项目(如画板、小游戏、数据可视化)。
项目开发流程
-
需求分析与技术选型
- 明确核心功能:如「画板」需支持画笔、橡皮擦、颜色选择、保存图片;「贪吃蛇游戏」需支持移动、碰撞检测、计分。
- 技术栈:纯Canvas API + JavaScript(基础项目);结合框架(如React/Vue,用于UI交互);工具库(如
fabric.js简化Canvas操作)。
-
核心功能实现
- 以「简易画板」为例:
- 画笔:监听
mousedown开始绘制,mousemove中用lineTo和stroke绘制连续线段。 - 橡皮擦:本质是用
clearRect或相同背景色的strokeStyle覆盖。 - 保存图片:
canvas.toDataURL('image/png')生成图片URL,通过<a>标签下载。
- 画笔:监听
// 画板核心代码(画笔功能) let isDrawing = false; let lastX = 0, lastY = 0; // 鼠标按下:开始绘制 canvas.addEventListener('mousedown', (e) => { isDrawing = true; [lastX, lastY] = getCanvasCoords(e); // 获取鼠标在Canvas中的坐标 }); // 鼠标移动:绘制线段 canvas.addEventListener('mousemove', (e) => { if (!isDrawing) return; const [x, y] = getCanvasCoords(e); ctx.beginPath(); ctx.moveTo(lastX, lastY); ctx.lineTo(x, y); ctx.stroke(); [lastX, lastY] = [x, y]; // 更新起点 }); // 鼠标释放:结束绘制 canvas.addEventListener('mouseup', () => isDrawing = false); canvas.addEventListener('mouseout', () => isDrawing = false); // 保存图片 document.getElementById('saveBtn').addEventListener('click', () => { const link = document.createElement('a'); link.download = 'canvas-drawing.png'; link.href = canvas.toDataURL('image/png'); link.click(); }); - 以「简易画板」为例:
-
测试与优化
- 兼容性测试:Canvas在现代浏览器支持良好,但需注意老旧浏览器(如IE9-不支持)的降级处理。
- 性能测试:复杂动画(如100+小球)需用离屏渲染、减少绘制频率等优化。
- 移动端适配:处理触摸事件,设置
viewport确保Canvas尺寸正确。
注意事项
- 大型项目建议使用Canvas框架(如
fabric.js、Konva.js),简化图层管理、事件处理等复杂逻辑。 - 保存图片时,
toDataURL受同源策略限制(绘制跨域图片会导致tainted canvas,无法导出)。
总结
HTML5 Canvas学习路径呈阶梯式递进:从「绘制基础图形」到「控制样式与图像」,再到「实现交互与动画」,最终通过「高级特性优化」完成项目开发。核心是理解「画布是像素的集合」,所有操作本质是对像素的读写与计算。掌握基础API后,创意和性能优化是区分项目质量的关键。

1550

被折叠的 条评论
为什么被折叠?



