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

服务端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月29日 01:55
Canvas 动画怎么实现?如何优化性能?Canvas 动画本质是逐帧重绘:每帧调用 `requestAnimationFrame` 回调,在其中更新状态、清除画布、重绘所有元素,浏览器按刷新率(通常 60fps)将帧提交到屏幕。性能优化的核心思路是减少每帧的计算量和绘制量:用 `OffscreenCanvas` + Worker 将计算和预渲染移到后台线程;用对象池复用粒子避免 GC 压力;用脏矩形只重绘变化区域;分层 Canvas 将静态背景和动态前景分离;批量绘制减少状态切换。
## 追问
- `requestAnimationFrame` 和 `setInterval` 做动画有什么本质区别?
- `OffscreenCanvas` 在 Worker 中如何与主线程 Canvas 同步?
- 对象池模式怎么实现?复用对象时要注意清理哪些字段?
- 什么是脏矩形优化?在什么场景下效果明显?
- 分层 Canvas 的代价是什么?层太多反而更慢吗?
## 写段代码
```js
// 离屏预渲染 + requestAnimationFrame
const offscreen = new OffscreenCanvas(60, 60)
const offCtx = offscreen.getContext('2d')
offCtx.fillStyle = 'coral'
offCtx.beginPath()
offCtx.arc(30, 30, 25, 0, Math.PI * 2)
offCtx.fill()
let x = 0
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(offscreen, x, 100)
x = (x + 2) % canvas.width
requestAnimationFrame(animate)
}
animate()
```服务端5月29日 01:55
Canvas 元素是什么?和 SVG 有什么区别?Canvas 是 HTML5 的 `<canvas>` 元素,提供一块可编程的像素级绘图表面。通过 JS 获取 2D 渲染上下文(`getContext('2d')`)或 WebGL 上下文来绘制图形。核心用途:数据可视化、2D 游戏、图像处理、动画、视频特效。与 SVG 的关键区别:Canvas 是栅格模式(像素绘制,适合高频更新和大量对象),SVG 是矢量模式(DOM 节点,适合交互和缩放)。Canvas 不保留绘制对象,每帧是完整重绘。
## 追问
- Canvas 2D 和 WebGL 上下文有什么区别?什么场景需要用 WebGL?
- Canvas 重绘时旧内容去哪了?为什么说 Canvas 是"立即模式"渲染?
- Canvas 如何实现像素级操作?`getImageData` 和 `putImageData` 的性能如何?
- Canvas 的 `width/height` 属性和 CSS 宽高不一致时会怎样?
- 什么情况下应该选 SVG 而不是 Canvas?
## 写段代码
```js
const canvas = document.querySelector('canvas')
canvas.width = 400 // 设置分辨率
canvas.height = 300
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#4a90d9'
ctx.fillRect(10, 10, 100, 80) // 绘制矩形
ctx.font = '16px sans-serif'
ctx.fillText('Hello Canvas', 120, 50) // 绘制文字
```服务端5月29日 01:55
Canvas 路径绘制和图形变换怎么用?路径绘制分三步:定义路径、闭合路径、渲染路径。先用 `beginPath()` 清除旧路径,`moveTo()` 定起点,再用 `lineTo()`/`arc()`/`bezierCurveTo()` 等绘制子路径,最后 `closePath()` 闭合并用 `fill()` 或 `stroke()` 渲染。图形变换通过坐标系变换实现:`translate(x,y)` 平移原点、`rotate(angle)` 绕原点旋转、`scale(sx,sy)` 缩放,`transform(a,b,c,d,e,f)` 直接设置变换矩阵。变换是累积的,用 `save()/restore()` 管理状态栈,避免污染后续绘制。
## 追问
- `beginPath()` 和 `closePath()` 的区别是什么?不调用 `closePath()` 会怎样?
- `save()/restore()` 保存了哪些状态?变换矩阵和路径都会保存吗?
- `transform()` 和 `setTransform()` 有什么区别?
- 多次 `translate()` 后坐标系如何变化?如何回到初始状态?
- 如何用贝塞尔曲线绘制平滑曲线?控制点怎么选?
## 写段代码
```js
ctx.save()
ctx.translate(150, 150) // 移动原点到画布中心
ctx.rotate(Math.PI / 6) // 旋转30度
ctx.fillStyle = 'tomato'
ctx.fillRect(-40, -40, 80, 80) // 以新原点为中心绘制
ctx.restore() // 恢复原始坐标系
```服务端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)` 缩放上下文。前端2024年7月17日 22:11
如何在HTML5 Canvas上绘制多边形?在HTML5 Canvas上绘制多边形,您可以遵循以下步骤:
1. **创建画布**:
首先,在HTML文件中添加一个`<canvas>`元素来创建一个画布。
```html
<canvas id="myCanvas" width="500" height="500"></canvas>
```
2. **获取画布上下文**:
在JavaScript中,使用`getContext()`方法获取画布的2D渲染上下文。
```javascript
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
```
3. **绘制多边形**:
使用`beginPath()`方法开始一个新的路径,然后使用`moveTo()`将画笔移动到多边形的起始点。接着使用`lineTo()`方法添加多个线段,最后用`closePath()`闭合路径。
```javascript
ctx.beginPath();
ctx.moveTo(x1, y1); // 第一个顶点
ctx.lineTo(x2, y2); // 第二个顶点
ctx.lineTo(x3, y3); // 第三个顶点
// 继续添加更多顶点
ctx.closePath(); // 闭合路径
```
4. **设置样式和填充**:
可以通过设置`strokeStyle`和`fillStyle`属性来自定义多边形的边框和填充颜色,然后使用`stroke()`和`fill()`方法对形状进行描边和填充。
```javascript
ctx.strokeStyle = 'blue'; // 边框颜色
ctx.fillStyle = 'red'; // 填充颜色
ctx.stroke(); // 描边
ctx.fill(); // 填充
```
以上就是在HTML5 Canvas上绘制一个多边形的基本步骤。您可以通过改变`lineTo()`方法中的坐标点来控制多边形的形状和大小。