pin_drop当前位置:知识文库 ❯ 图文
多媒体:Canvas合成 - 完整教程与代码示例
一、教程简介
Canvas 合成模式(Compositing)决定了新绘制的图形如何与已有内容进行混合。通过 globalCompositeOperation 属性,可以实现各种视觉效果,如透明叠加、遮罩、裁切、混合模式等。本教程将详细介绍所有合成模式的原理、效果以及实际应用场景。
二、核心概念
源与目标
在合成操作中,有两个关键概念:
-
源(Source):即将绘制的新图形
-
目标(Destination):画布上已有的内容
代码示例
┌─────────────┐
│ Destination │ ← 已有内容(目标)
│ ┌─────┐ │
│ │Source│ │ ← 新绘制内容(源)
│ └─────┘ │
└─────────────┘合成模式的分类
三、语法与用法
属性
所有合成模式
基本合成模式
混合模式
四、代码示例
基本合成模式对比
代码示例
<!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 {
margin: 0;
padding: 20px;
background: #0d1117;
font-family: "Microsoft YaHei", sans-serif;
color: #c9d1d9;
}
h1 { text-align: center; color: #f0883e; }
canvas {
display: block;
margin: 20px auto;
border: 1px solid #30363d;
border-radius: 8px;
}
</style>
</head>
<body>
<h1>Canvas 合成模式对比</h1>
<canvas id="compositeCanvas" width="750" height="700"></canvas>
<script>
const canvas = document.getElementById('compositeCanvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#161b22';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const modes = [
'source-over', 'source-in', 'source-out', 'source-atop',
'destination-over', 'destination-in', 'destination-out', 'destination-atop',
'lighter', 'copy', 'xor', 'multiply',
'screen', 'overlay', 'darken', 'lighten',
'color-dodge', 'color-burn', 'hard-light', 'soft-light',
'difference', 'exclusion', 'hue', 'color'
];
const cols = 4;
const cellW = 170;
const cellH = 130;
const gap = 10;
modes.forEach((mode, i) => {
const col = i % cols;
const row = Math.floor(i / cols);
const x = 15 + col * (cellW + gap);
const y = 15 + row * (cellH + gap);
// 背景
ctx.fillStyle = '#0d1117';
ctx.fillRect(x, y, cellW, cellH - 20);
// 目标(蓝色矩形)
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = '#539bf5';
ctx.beginPath();
ctx.arc(x + cellW * 0.38, y + (cellH - 20) * 0.45, 35, 0, Math.PI * 2);
ctx.fill();
// 源(红色矩形)- 使用指定合成模式
ctx.globalCompositeOperation = mode;
ctx.fillStyle = '#f47067';
ctx.beginPath();
ctx.arc(x + cellW * 0.62, y + (cellH - 20) * 0.55, 35, 0, Math.PI * 2);
ctx.fill();
// 重置合成模式
ctx.globalCompositeOperation = 'source-over';
// 标签
ctx.fillStyle = '#8b949e';
ctx.font = '11px "Microsoft YaHei"';
ctx.textAlign = 'center';
ctx.fillText(mode, x + cellW / 2, y + cellH - 5);
});
</script>
</body>
</html>合成模式实战 - 遮罩与擦除效果
代码示例
<!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>
body {
margin: 0;
padding: 20px;
background: #1a1a2e;
font-family: "Microsoft YaHei", sans-serif;
color: #e0e0e0;
}
h1 { text-align: center; color: #e94560; }
.demo-row {
display: flex;
justify-content: center;
gap: 30px;
flex-wrap: wrap;
margin-top: 20px;
}
canvas {
border: 1px solid #333;
border-radius: 8px;
cursor: crosshair;
}
.label { text-align: center; color: #888; font-size: 14px; margin-top: 8px; }
</style>
</head>
<body>
<h1>合成模式实战 - 遮罩与擦除</h1>
<div class="demo-row">
<div>
<canvas id="maskCanvas" width="300" height="300"></canvas>
<p class="label">destination-in 遮罩效果</p>
</div>
<div>
<canvas id="eraseCanvas" width="300" height="300"></canvas>
<p class="label">destination-out 擦除效果(点击拖拽)</p>
</div>
</div>
<script>
// === 遮罩效果 ===
const maskCanvas = document.getElementById('maskCanvas');
const maskCtx = maskCanvas.getContext('2d');
// 绘制彩色背景
const grad = maskCtx.createLinearGradient(0, 0, 300, 300);
grad.addColorStop(0, '#e94560');
grad.addColorStop(0.5, '#0f3460');
grad.addColorStop(1, '#533483');
maskCtx.fillStyle = grad;
maskCtx.fillRect(0, 0, 300, 300);
// 添加一些装饰
for (let i = 0; i < 20; i++) {
maskCtx.beginPath();
maskCtx.arc(Math.random() * 300, Math.random() * 300, Math.random() * 20 + 5, 0, Math.PI * 2);
maskCtx.fillStyle = `rgba(255,255,255,${Math.random() * 0.3})`;
maskCtx.fill();
}
// 使用 destination-in 创建遮罩
maskCtx.globalCompositeOperation = 'destination-in';
maskCtx.beginPath();
maskCtx.arc(150, 150, 120, 0, Math.PI * 2);
maskCtx.fill();
maskCtx.globalCompositeOperation = 'source-over';
// 添加边框
maskCtx.strokeStyle = '#e94560';
maskCtx.lineWidth = 3;
maskCtx.beginPath();
maskCtx.arc(150, 150, 120, 0, Math.PI * 2);
maskCtx.stroke();
// === 擦除效果 ===
const eraseCanvas = document.getElementById('eraseCanvas');
const eraseCtx = eraseCanvas.getContext('2d');
// 绘制初始内容
const eGrad = eraseCtx.createRadialGradient(150, 150, 0, 150, 150, 200);
eGrad.addColorStop(0, '#ffd93d');
eGrad.addColorStop(0.5, '#4CAF50');
eGrad.addColorStop(1, '#2196F3');
eraseCtx.fillStyle = eGrad;
eraseCtx.fillRect(0, 0, 300, 300);
eraseCtx.fillStyle = '#fff';
eraseCtx.font = 'bold 20px "Microsoft YaHei"';
eraseCtx.textAlign = 'center';
eraseCtx.textBaseline = 'middle';
eraseCtx.fillText('拖拽擦除', 150, 150);
let isErasing = false;
eraseCanvas.addEventListener('mousedown', (e) => {
isErasing = true;
erase(e);
});
eraseCanvas.addEventListener('mousemove', (e) => {
if (isErasing) erase(e);
});
eraseCanvas.addEventListener('mouseup', () => { isErasing = false; });
eraseCanvas.addEventListener('mouseleave', () => { isErasing = false; });
function erase(e) {
const rect = eraseCanvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
eraseCtx.globalCompositeOperation = 'destination-out';
eraseCtx.beginPath();
eraseCtx.arc(x, y, 20, 0, Math.PI * 2);
eraseCtx.fill();
eraseCtx.globalCompositeOperation = 'source-over';
}
</script>
</body>
</html>五、浏览器兼容性
六、注意事项与最佳实践
1. 合成模式影响所有后续绘制
设置 globalCompositeOperation 后会影响所有后续绘制操作,务必在使用后重置:
代码示例
ctx.globalCompositeOperation = 'multiply';
// ... 绘制
ctx.globalCompositeOperation = 'source-over'; // 重置2. source-in 和 destination-in 的透明背景
source-in 和 destination-in 会将非重叠区域变为透明,如果 Canvas 背景是绘制上去的,也会被影响:
代码示例
// 先绘制背景(作为目标的一部分)
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制目标图形
ctx.fillStyle = 'blue';
ctx.fillRect(50, 50, 100, 100);
// source-in:只保留重叠部分
ctx.globalCompositeOperation = 'source-in';
ctx.fillStyle = 'red';
ctx.fillRect(80, 80, 100, 100);
// 结果:白色背景也会被裁切!3. 混合模式与透明度
混合模式在半透明颜色下效果更明显:
代码示例
ctx.globalAlpha = 0.5;
ctx.globalCompositeOperation = 'multiply';
// 效果更柔和4. 性能考虑
某些合成模式(如混合模式)比 source-over 更耗性能,在大量绘制时需注意。
5. 擦除效果的最佳实践
使用 destination-out 实现擦除时,确保擦除的"画笔"颜色是不透明的:
代码示例
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'rgba(0,0,0,1)'; // 必须完全不透明
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();七、代码规范示例
代码示例
/**
* 合成模式工具类
*/
class CompositeHelper {
constructor(ctx) {
this.ctx = ctx;
}
/**
* 使用指定合成模式绘制
* @param {string} mode - 合成模式
* @param {Function} drawFn - 绘制函数
*/
withMode(mode, drawFn) {
const ctx = this.ctx;
ctx.save();
ctx.globalCompositeOperation = mode;
drawFn(ctx);
ctx.restore();
}
/**
* 创建遮罩效果
* @param {Function} contentFn - 绘制内容
* @param {Function} maskFn - 绘制遮罩形状
*/
mask(contentFn, maskFn) {
const ctx = this.ctx;
ctx.save();
contentFn(ctx);
ctx.globalCompositeOperation = 'destination-in';
maskFn(ctx);
ctx.restore();
ctx.globalCompositeOperation = 'source-over';
}
/**
* 擦除区域
* @param {number} x - X 坐标
* @param {number} y - Y 坐标
* @param {number} radius - 擦除半径
*/
erase(x, y, radius) {
const ctx = this.ctx;
ctx.save();
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}八、常见问题与解决方案
常见问题
source-in 后图形消失怎么办?
原因是源和目标没有重叠区域。解决方案是确保源和目标有重叠部分,调整绘制位置使两者产生交集。
混合模式效果不明显怎么解决?
原因是颜色太暗或太亮,或透明度设置不当。解决方案是使用更饱和的颜色和适当的透明度,半透明颜色下混合模式效果更明显。
擦除后无法恢复怎么办?
原因是 destination-out 是破坏性操作。解决方案是使用离屏 Canvas 保存原始内容,或在使用擦除前通过 save/restore 管理状态。
合成模式在 IE 中不工作如何处理?
IE 不支持混合模式(multiply、screen 等),需要检测浏览器支持情况并提供降级方案,可以使用基本的 source-over 等模式作为替代。
九、总结
Canvas 合成模式是创建高级视觉效果的重要工具:
-
globalCompositeOperation:控制新绘制内容与已有内容的混合方式
-
基本模式:source-over(默认)、source-in、destination-out 等
-
混合模式:multiply、screen、overlay 等,类似 Photoshop 图层混合
-
遮罩效果:使用 destination-in 限制内容显示区域
-
擦除效果:使用 destination-out 擦除已有内容
-
lighter:颜色值相加,适合发光效果
-
xor:重叠区域变透明,适合创建镂空效果
-
务必重置:使用后重置为 source-over
小贴士
Canvas 合成模式与 Photoshop 的图层混合模式原理相同。如果你在 Photoshop 中熟悉正片叠底(multiply)、滤色(screen)等混合模式,那么在 Canvas 中使用它们会非常直观。建议在开发工具中实际测试每种模式的效果,以便在项目中选择最合适的合成方式。
本文涉及AI创作
内容由AI创作,请仔细甄别