pin_drop当前位置:知识文库 ❯ 图文
多媒体:渐变 - 完整教程与代码示例
一、教程简介
渐变是 Canvas 中创建丰富视觉效果的重要手段。Canvas 支持两种渐变类型:线性渐变(createLinearGradient)和径向渐变(createRadialGradient)。渐变可以作为 fillStyle 或 strokeStyle 使用,为图形、文本和路径添加平滑的颜色过渡效果。本教程将详细讲解渐变的创建、配置和使用方法。
二、核心概念
线性渐变(Linear Gradient)
线性渐变沿一条直线方向进行颜色过渡。通过指定起点和终点坐标定义渐变方向。
径向渐变(Radial Gradient)
径向渐变在两个圆之间进行颜色过渡。通过指定内圆和外圆的圆心与半径来定义渐变区域。
色标(Color Stop)
色标定义渐变中的关键颜色点,每个色标包含:
-
offset:位置偏移量,0~1 之间的数值
-
color:颜色值,支持所有 CSS 颜色格式
色标必须按 offset 升序添加。
三、语法与用法
createLinearGradient - 创建线性渐变
代码示例
const gradient = ctx.createLinearGradient(x0, y0, x1, y1);createRadialGradient - 创建径向渐变
代码示例
const gradient = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);addColorStop - 添加色标
代码示例
gradient.addColorStop(offset, color);使用渐变
代码示例
// 作为填充样式
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);
// 作为描边样式
ctx.strokeStyle = gradient;
ctx.strokeRect(10, 130, 200, 100);四、代码示例
示例1:线性渐变全面演示
代码示例
<!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: #0d1117;
color: #c9d1d9;
font-family: 'Segoe UI', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
}
canvas {
border: 1px solid #30363d;
border-radius: 8px;
background: #161b22;
}
</style>
</head>
<body>
<h1>线性渐变全面演示</h1>
<canvas id="myCanvas" width="750" height="500"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// === 1. 水平渐变 ===
ctx.fillStyle = '#c9d1d9';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('水平渐变 (左→右)', 125, 25);
const hGrad = ctx.createLinearGradient(0, 0, 250, 0);
hGrad.addColorStop(0, '#e94560');
hGrad.addColorStop(0.5, '#f8b500');
hGrad.addColorStop(1, '#3fb950');
ctx.fillStyle = hGrad;
ctx.fillRect(10, 40, 230, 80);
// === 2. 垂直渐变 ===
ctx.fillStyle = '#c9d1d9';
ctx.fillText('垂直渐变 (上→下)', 375, 25);
const vGrad = ctx.createLinearGradient(0, 40, 0, 120);
vGrad.addColorStop(0, '#58a6ff');
vGrad.addColorStop(1, '#1f6feb');
ctx.fillStyle = vGrad;
ctx.fillRect(260, 40, 230, 80);
// === 3. 对角渐变 ===
ctx.fillStyle = '#c9d1d9';
ctx.fillText('对角渐变', 625, 25);
const dGrad = ctx.createLinearGradient(510, 40, 740, 120);
dGrad.addColorStop(0, '#d2a8ff');
dGrad.addColorStop(1, '#f78166');
ctx.fillStyle = dGrad;
ctx.fillRect(510, 40, 230, 80);
// === 4. 多色标渐变 ===
ctx.fillStyle = '#c9d1d9';
ctx.font = 'bold 14px sans-serif';
ctx.fillText('多色标渐变 (彩虹)', 375, 155);
const rainbow = ctx.createLinearGradient(10, 0, 740, 0);
rainbow.addColorStop(0, '#e94560');
rainbow.addColorStop(0.17, '#f8b500');
rainbow.addColorStop(0.33, '#3fb950');
rainbow.addColorStop(0.5, '#58a6ff');
rainbow.addColorStop(0.67, '#d2a8ff');
rainbow.addColorStop(0.83, '#f78166');
rainbow.addColorStop(1, '#e94560');
ctx.fillStyle = rainbow;
ctx.fillRect(10, 170, 730, 40);
// === 5. 渐变文字 ===
ctx.fillStyle = '#c9d1d9';
ctx.font = 'bold 14px sans-serif';
ctx.fillText('渐变文字', 375, 245);
const textGrad = ctx.createLinearGradient(150, 0, 600, 0);
textGrad.addColorStop(0, '#e94560');
textGrad.addColorStop(0.5, '#f8b500');
textGrad.addColorStop(1, '#58a6ff');
ctx.fillStyle = textGrad;
ctx.font = 'bold 48px sans-serif';
ctx.fillText('Gradient Text', 375, 295);
// === 6. 渐变描边 ===
ctx.fillStyle = '#c9d1d9';
ctx.font = 'bold 14px sans-serif';
ctx.fillText('渐变描边', 375, 335);
const strokeGrad = ctx.createLinearGradient(100, 0, 650, 0);
strokeGrad.addColorStop(0, '#3fb950');
strokeGrad.addColorStop(1, '#58a6ff');
ctx.strokeStyle = strokeGrad;
ctx.lineWidth = 3;
ctx.font = 'bold 40px sans-serif';
ctx.strokeText('Stroke Gradient', 375, 380);
// === 7. 渐变方向对比 ===
ctx.fillStyle = '#c9d1d9';
ctx.font = 'bold 14px sans-serif';
ctx.fillText('渐变方向对比', 375, 420);
const directions = [
{ x0: 0, y0: 0, x1: 150, y1: 0, label: '0 度' },
{ x0: 0, y0: 0, x1: 106, y1: 106, label: '45 度' },
{ x0: 0, y0: 0, x1: 0, y1: 150, label: '90 度' },
{ x0: 150, y0: 0, x1: 0, y1: 0, label: '180 度' }
];
directions.forEach((dir, i) => {
const x = 30 + i * 185;
const y = 440;
const grad = ctx.createLinearGradient(x + dir.x0, y + dir.y0, x + dir.x1, y + dir.y1);
grad.addColorStop(0, '#e94560');
grad.addColorStop(1, '#58a6ff');
ctx.fillStyle = grad;
ctx.fillRect(x, y, 150, 50);
ctx.fillStyle = '#c9d1d9';
ctx.font = '11px sans-serif';
ctx.fillText(dir.label, x + 75, y + 65);
});
</script>
</body>
</html>示例2:径向渐变与创意效果
代码示例
<!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;
color: #eee;
font-family: 'Segoe UI', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
}
canvas {
border: 1px solid #333;
border-radius: 8px;
background: #16213e;
}
</style>
</head>
<body>
<h1>径向渐变与创意效果</h1>
<canvas id="myCanvas" width="700" height="500"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// === 1. 基本径向渐变 ===
ctx.fillStyle = '#eee';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('基本径向渐变', 120, 25);
const rGrad1 = ctx.createRadialGradient(120, 100, 10, 120, 100, 80);
rGrad1.addColorStop(0, '#ffffff');
rGrad1.addColorStop(0.5, '#58a6ff');
rGrad1.addColorStop(1, '#1f6feb');
ctx.fillStyle = rGrad1;
ctx.beginPath();
ctx.arc(120, 100, 80, 0, Math.PI * 2);
ctx.fill();
// === 2. 偏心径向渐变(球体效果) ===
ctx.fillStyle = '#eee';
ctx.fillText('球体效果', 310, 25);
const rGrad2 = ctx.createRadialGradient(290, 80, 5, 310, 100, 80);
rGrad2.addColorStop(0, '#ffffff');
rGrad2.addColorStop(0.3, '#e94560');
rGrad2.addColorStop(1, '#5a1020');
ctx.fillStyle = rGrad2;
ctx.beginPath();
ctx.arc(310, 100, 80, 0, Math.PI * 2);
ctx.fill();
// === 3. 内圆大于外圆 ===
ctx.fillStyle = '#eee';
ctx.fillText('内圆偏移效果', 500, 25);
const rGrad3 = ctx.createRadialGradient(460, 80, 10, 500, 100, 80);
rGrad3.addColorStop(0, '#3fb950');
rGrad3.addColorStop(0.5, '#f8b500');
rGrad3.addColorStop(1, 'rgba(248, 181, 0, 0)');
ctx.fillStyle = rGrad3;
ctx.beginPath();
ctx.arc(500, 100, 80, 0, Math.PI * 2);
ctx.fill();
// === 4. 光晕效果 ===
ctx.fillStyle = '#eee';
ctx.font = 'bold 14px sans-serif';
ctx.fillText('光晕效果', 350, 210);
// 背景暗色
ctx.fillStyle = '#0a0a1a';
ctx.fillRect(50, 225, 600, 120);
// 多层光晕
const glowColors = [
{ x: 150, color: '#e94560' },
{ x: 350, color: '#58a6ff' },
{ x: 550, color: '#3fb950' }
];
glowColors.forEach(g => {
const glow = ctx.createRadialGradient(g.x, 285, 0, g.x, 285, 80);
glow.addColorStop(0, g.color);
glow.addColorStop(0.4, g.color + '80');
glow.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = glow;
ctx.fillRect(g.x - 80, 225, 160, 120);
});
// === 5. 渐变按钮 ===
ctx.fillStyle = '#eee';
ctx.font = 'bold 14px sans-serif';
ctx.fillText('渐变按钮', 350, 375);
function drawGradientButton(ctx, x, y, w, h, colors, text) {
const grad = ctx.createLinearGradient(x, y, x + w, y + h);
colors.forEach((c, i) => {
grad.addColorStop(i / (colors.length - 1), c);
});
// 圆角矩形
const r = 8;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
ctx.fillStyle = grad;
ctx.fill();
// 文字
ctx.fillStyle = '#fff';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(text, x + w / 2, y + h / 2);
}
drawGradientButton(ctx, 50, 390, 140, 40, ['#e94560', '#d63384'], 'Danger');
drawGradientButton(ctx, 210, 390, 140, 40, ['#58a6ff', '#1f6feb'], 'Primary');
drawGradientButton(ctx, 370, 390, 140, 40, ['#3fb950', '#238636'], 'Success');
drawGradientButton(ctx, 530, 390, 140, 40, ['#f8b500', '#d29922'], 'Warning');
// === 6. 渐变背景 ===
ctx.fillStyle = '#eee';
ctx.font = 'bold 14px sans-serif';
ctx.textBaseline = 'alphabetic';
ctx.fillText('渐变背景卡片', 350, 455);
// 卡片1 - 日落渐变
const sunset = ctx.createLinearGradient(50, 470, 230, 560);
sunset.addColorStop(0, '#e94560');
sunset.addColorStop(0.5, '#f8b500');
sunset.addColorStop(1, '#3fb950');
ctx.fillStyle = sunset;
ctx.fillRect(50, 470, 180, 25);
// 卡片2 - 海洋渐变
const ocean = ctx.createLinearGradient(260, 470, 440, 560);
ocean.addColorStop(0, '#0c3547');
ocean.addColorStop(0.5, '#1f6feb');
ocean.addColorStop(1, '#58a6ff');
ctx.fillStyle = ocean;
ctx.fillRect(260, 470, 180, 25);
// 卡片3 - 极光渐变
const aurora = ctx.createLinearGradient(470, 470, 650, 560);
aurora.addColorStop(0, '#d2a8ff');
aurora.addColorStop(0.5, '#58a6ff');
aurora.addColorStop(1, '#3fb950');
ctx.fillStyle = aurora;
ctx.fillRect(470, 470, 180, 25);
</script>
</body>
</html>五、浏览器兼容性
六、注意事项与最佳实践
1. 渐变坐标是全局坐标
渐变的坐标是相对于 Canvas 画布的,不是相对于使用渐变的图形:
代码示例
// 渐变坐标是全局的
const grad = ctx.createLinearGradient(0, 0, 200, 0);
grad.addColorStop(0, 'red');
grad.addColorStop(1, 'blue');
// 在不同位置使用同一渐变,效果不同
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 50); // 完整渐变
ctx.fillRect(100, 60, 200, 50); // 只显示渐变的后半部分2. 色标顺序必须升序
代码示例
// 正确:offset 升序
gradient.addColorStop(0, 'red');
gradient.addColorStop(0.5, 'yellow');
gradient.addColorStop(1, 'green');
// 错误:offset 不是升序会抛出异常
// gradient.addColorStop(0.5, 'yellow');
// gradient.addColorStop(0.3, 'red'); // IndexSizeError3. 颜色格式统一
代码示例
// 推荐:使用标准格式
gradient.addColorStop(0, '#e94560');
gradient.addColorStop(1, 'rgba(88, 166, 255, 0.8)');
// 不推荐:混合格式可能导致问题
// gradient.addColorStop(0, 'red');
// gradient.addColorStop(1, '#58a6ff');4. 透明渐变
代码示例
// 渐变到透明
const fadeGrad = ctx.createLinearGradient(0, 0, 200, 0);
fadeGrad.addColorStop(0, 'rgba(88, 166, 255, 1)');
fadeGrad.addColorStop(1, 'rgba(88, 166, 255, 0)');5. 渐变对象可复用
代码示例
const grad = ctx.createLinearGradient(0, 0, 200, 0);
grad.addColorStop(0, 'red');
grad.addColorStop(1, 'blue');
ctx.fillStyle = grad;
ctx.fillRect(10, 10, 200, 50);
ctx.strokeStyle = grad;
ctx.lineWidth = 3;
ctx.strokeRect(10, 80, 200, 50);七、代码规范示例
代码示例
// 推荐:封装渐变创建
function createLinearGrad(ctx, x0, y0, x1, y1, stops) {
const grad = ctx.createLinearGradient(x0, y0, x1, y1);
stops.forEach(([offset, color]) => {
grad.addColorStop(offset, color);
});
return grad;
}
function createRadialGrad(ctx, x0, y0, r0, x1, y1, r1, stops) {
const grad = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
stops.forEach(([offset, color]) => {
grad.addColorStop(offset, color);
});
return grad;
}
// 推荐:预设渐变方案
const GRADIENT_PRESETS = {
sunset: [[0, '#e94560'], [0.5, '#f8b500'], [1, '#3fb950']],
ocean: [[0, '#0c3547'], [0.5, '#1f6feb'], [1, '#58a6ff']],
aurora: [[0, '#d2a8ff'], [0.5, '#58a6ff'], [1, '#3fb950']],
fire: [[0, '#ff0000'], [0.5, '#ff8800'], [1, '#ffcc00']],
neon: [[0, '#00ff88'], [0.5, '#00aaff'], [1, '#8855ff']]
};
// 使用示例
const grad = createLinearGrad(ctx, 0, 0, 400, 0, GRADIENT_PRESETS.sunset);
ctx.fillStyle = grad;
ctx.fillRect(10, 10, 400, 100);八、常见问题与解决方案
常见问题
渐变显示的位置不对?
原因:渐变坐标是全局坐标,不是相对于图形的局部坐标。解决方案:根据图形位置计算渐变坐标,或使用变换让渐变跟随图形。
addColorStop 报错?
原因:offset不在0~1范围内,或颜色格式无效。解决方案:确保offset在0~1之间,并使用标准颜色格式。
如何创建锥形渐变?
Canvas 2D API没有原生的锥形渐变。解决方案:使用createConicGradient(现代浏览器支持)。
径向渐变如何实现聚光灯效果?
偏移内圆位置,模拟光源方向。内圆偏左上作为光源位置,外圆居中。
渐变可以用于阴影吗?
阴影颜色不支持渐变,但可以通过多层绘制模拟渐变阴影效果。
九、总结
渐变是 Canvas 创建丰富视觉效果的核心工具,关键要点:
-
createLinearGradient:创建线性渐变,通过起止点定义方向
-
createRadialGradient:创建径向渐变,通过内外圆定义区域
-
addColorStop:添加色标,offset 必须在 0~1 之间且升序
-
全局坐标:渐变坐标相对于画布,不是相对于图形
-
可复用:渐变对象可同时用于 fillStyle 和 strokeStyle
-
透明渐变:使用 rgba 实现渐变到透明的效果
-
创意应用:球体效果、光晕、渐变文字、渐变按钮等
下一教程将介绍 Canvas 图案的使用。
本文涉及AI创作
内容由AI创作,请仔细甄别