标签

Canvas

HTML5 <canvas> 元素是一个可以使用脚本(通常是JavaScript)来绘制图形和动画的HTML元素。它是HTML5规范的一部分,旨在提供一个丰富的图形绘制接口,允许开发者绘制2D图形,从简单的图形和文本到复杂的动画和游戏场景。

Canvas
服务端5月30日 00:10
Canvas 如何进行图像处理和像素操作?Canvas 图像处理就是四步:`drawImage` 把图片、视频帧或另一个 Canvas 画上去;`getImageData` 读出像素;修改 `data` 里的 RGBA;再用 `putImageData` 写回。`data` 是 `Uint8ClampedArray`,每个像素 4 个值,位置从 `i * 4` 开始。九参数 `drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)` 可同时裁剪和缩放,雪碧图截帧、头像裁剪常用。跨域图必须服务端开 CORS,并给图片设置 `crossOrigin="anonymous"`,否则读像素会抛 `SecurityError`。 ## 追问 ### Canvas 橡皮擦怎么做? 把 `globalCompositeOperation` 设成 `destination-out`,新画的区域会从旧内容里挖掉。蒙版常用 `source-in`,发光叠加可用 `lighter`。 ### 灰度滤镜为什么不用 RGB 平均值? 人眼对绿色更敏感,常用 `0.299R + 0.587G + 0.114B` 算亮度,视觉效果比简单平均自然。 ### 大图逐像素处理很卡怎么办? 用 OffscreenCanvas + Worker,把像素循环放到子线程;静态图层先离屏缓存;只改局部时用 `putImageData` 的脏矩形参数,别全画布回写。 ### 处理后的图片怎么导出? 推荐 `canvas.toBlob(cb, "image/jpeg", 0.92)`,异步且省内存。`toDataURL()` 同步生成 Base64,只适合小图预览。 ## 写段代码 ```javascript function grayscale(imageData) { const d = imageData.data; for (let i = 0; i < d.length; i += 4) { const gray = d[i] * 0.299 + d[i + 1] * 0.587 + d[i + 2] * 0.114; d[i] = d[i + 1] = d[i + 2] = gray; } return imageData; } ```
服务端5月29日 22:54
Canvas fillText 和 strokeText 有什么区别?如何实现高级文本效果?## Canvas fillText 和 strokeText 有什么区别?如何实现高级文本效果? **fillText** 绘制实心文字,**strokeText** 绘制文字轮廓(空心),两者共用 font、textAlign、textBaseline 属性。调用签名:`fillText(text, x, y [, maxWidth])`,maxWidth 超出时自动压缩。 关键要点: - **同时使用**:先 strokeText 再 fillText,可做出描边+填充效果,避免填充被描边覆盖 - **measureText**:获取文本宽度做布局计算,返回 TextMetrics 对象 - **maxWidth**:文本超宽时等比缩放,不截断 - **多行文本**:Canvas 不支持自动换行,需手动按 \n 分割后逐行 fillText - **中文渲染**:指定支持中文的字体族,否则可能回退为系统默认 ```javascript ctx.font = 'bold 24px sans-serif'; ctx.strokeStyle = '#333'; ctx.lineWidth = 2; ctx.strokeText('描边文字', 50, 50); ctx.fillStyle = '#e74c3c'; ctx.fillText('填充文字', 50, 80); ``` ### 追问 1. **如何实现文本自动换行?** — 按字符逐个累加 measureText 宽度,超过容器宽度时截断换行 2. **strokeText 描边模糊怎么办?** — 设置 lineJoin 为 round,或在 fillText 之前 strokeText 避免叠加伪影 3. **如何绘制竖排文字?** — 逐字绘制并递增 y 坐标,或使用 CSS WritingMode 配合 OffscreenCanvas 4. **measureText 能获取高度吗?** — 标准 TextMetrics 已支持 actualBoundingBoxAscent/Descent,兼容性需注意 5. **高 DPI 屏幕文字模糊如何解决?** — Canvas 尺寸乘 devicePixelRatio,CSS 尺寸不变,scale 后绘制
服务端5月28日 02:57
如何获取 Canvas 的 2D 上下文并使用基本绘制方法?## 获取 Canvas 2D 上下文 通过 `canvas.getContext('2d')` 获取 2D 渲染上下文,返回 `CanvasRenderingContext2D` 对象: ```javascript const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); ``` 实际开发中建议做兼容性检查: ```javascript const canvas = document.querySelector('canvas'); if (!canvas?.getContext) { throw new Error('当前浏览器不支持 Canvas'); } const ctx = canvas.getContext('2d'); ``` **注意**:同一个 Canvas 元素多次调用 `getContext('2d')` 返回的是同一个上下文对象,不会重复创建。 ## 基本绘制方法分类 Canvas 2D 的绘制方法可以按用途分为以下几类: ### 矩形绘制 矩形是 Canvas 中唯一可以直接绘制的图形,不需要路径: | 方法 | 说明 | |------|------| | `fillRect(x, y, w, h)` | 绘制填充矩形 | | `strokeRect(x, y, w, h)` | 绘制矩形边框 | | `clearRect(x, y, w, h)` | 清除矩形区域(变为透明) | ### 路径绘制 所有非矩形图形都需要通过路径来绘制: ```javascript ctx.beginPath(); // 开始新路径 ctx.moveTo(50, 50); // 移动画笔到起点 ctx.lineTo(200, 50); // 画直线到 (200, 50) ctx.arc(150, 100, 40, 0, Math.PI * 2); // 画圆弧 ctx.closePath(); // 闭合路径 ctx.fill(); // 填充 ctx.stroke(); // 描边 ``` 核心路径方法: - `beginPath()` — 开始新路径(不会清除已有路径) - `moveTo(x, y)` / `lineTo(x, y)` — 移动/画直线 - `arc(x, y, r, startAngle, endAngle)` — 画圆弧或圆 - `closePath()` — 从当前点回到路径起点 - `fill()` / `stroke()` — 填充或描边当前路径 ### 样式设置 绘制前设置样式,影响后续所有绘制操作: ```javascript ctx.fillStyle = '#ff6600'; // 填充颜色 ctx.strokeStyle = 'rgba(0,0,255,0.8)'; // 描边颜色 ctx.lineWidth = 3; // 线宽 ctx.lineCap = 'round'; // 线帽样式:butt | round | square ctx.lineJoin = 'miter'; // 连接样式:miter | round | bevel ``` ### 文本绘制 ```javascript ctx.font = '24px sans-serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText('Hello Canvas', 100, 100); // 填充文本 ctx.strokeText('Hello Canvas', 100, 100); // 描边文本 ``` 配合 `measureText()` 可以精确计算文本宽度: ```javascript const metrics = ctx.measureText('Hello'); console.log(metrics.width); // 文本像素宽度 ``` ### 图像绘制 `drawImage()` 支持三种调用方式: ```javascript // 基础:原尺寸绘制 ctx.drawImage(img, dx, dy); // 缩放:指定目标尺寸 ctx.drawImage(img, dx, dy, dWidth, dHeight); // 裁剪:从源图裁剪区域绘制到目标区域 ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); ``` ## 状态保存与恢复 这是面试高频考点。Canvas 通过栈结构管理绘制状态: ```javascript ctx.save(); // 将当前状态(样式、变换等)压入栈 // ... 修改样式、变换 ... ctx.restore(); // 从栈中弹出并恢复最近一次 save 的状态 ``` 典型场景:绘制多个不同样式的图形时,用 `save/restore` 避免样式互相污染。 ## 一个完整示例 把上面的方法组合起来,绘制一个带标题的彩色柱状图: ```javascript const canvas = document.getElementById('chart'); const ctx = canvas.getContext('2d'); const data = [ { label: 'A', value: 120, color: '#ff6600' }, { label: 'B', value: 80, color: '#0066ff' }, { label: 'C', value: 150, color: '#00cc66' }, ]; const barWidth = 60; const gap = 30; const baseY = 250; data.forEach((item, i) => { const x = 40 + i * (barWidth + gap); const height = item.value; // 绘制柱子 ctx.fillStyle = item.color; ctx.fillRect(x, baseY - height, barWidth, height); // 绘制标签 ctx.save(); ctx.fillStyle = '#333'; ctx.font = '14px sans-serif'; ctx.textAlign = 'center'; ctx.fillText(item.label, x + barWidth / 2, baseY + 20); ctx.fillText(String(item.value), x + barWidth / 2, baseY - height - 8); ctx.restore(); }); ``` ## 常见追问 **Q:Canvas 和 SVG 的区别是什么?** Canvas 是像素级绘制,适合高频重绘场景(游戏、图表动画);SVG 是矢量图形,通过 DOM 操作,适合交互式静态图形。Canvas 绘制后无法单独操作某个图形元素,SVG 可以。 **Q:如何实现 Canvas 动画?** 核心思路是清空画布 + 重绘。用 `clearRect` 清除上一帧,再绘制新帧,配合 `requestAnimationFrame` 控制帧率。需要避免在每帧中创建对象,减少 GC 压力。 **Q:Canvas 绘制模糊怎么解决?** 这是高 DPI 屏幕的常见问题。需要将 Canvas 的实际像素尺寸设为 CSS 尺寸的 `devicePixelRatio` 倍,再用 `ctx.scale(dpr, dpr)` 缩放上下文。