pin_drop当前位置:知识文库 ❯ 图文

多媒体:渐变 - 完整教程与代码示例

一、教程简介

渐变是 Canvas 中创建丰富视觉效果的重要手段。Canvas 支持两种渐变类型:线性渐变(createLinearGradient)和径向渐变(createRadialGradient)。渐变可以作为 fillStylestrokeStyle 使用,为图形、文本和路径添加平滑的颜色过渡效果。本教程将详细讲解渐变的创建、配置和使用方法。


二、核心概念

线性渐变(Linear Gradient)

线性渐变沿一条直线方向进行颜色过渡。通过指定起点和终点坐标定义渐变方向。

径向渐变(Radial Gradient)

径向渐变在两个圆之间进行颜色过渡。通过指定内圆和外圆的圆心与半径来定义渐变区域。

色标(Color Stop)

色标定义渐变中的关键颜色点,每个色标包含:

  • offset:位置偏移量,0~1 之间的数值

  • color:颜色值,支持所有 CSS 颜色格式

色标必须按 offset 升序添加。


三、语法与用法

createLinearGradient - 创建线性渐变

代码示例

const gradient = ctx.createLinearGradient(x0, y0, x1, y1);
参数 说明
x0, y0 渐变起点坐标
x1, y1 渐变终点坐标

createRadialGradient - 创建径向渐变

代码示例

const gradient = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
参数 说明
x0, y0 内圆(起始圆)圆心坐标
r0 内圆半径
x1, y1 外圆(结束圆)圆心坐标
r1 外圆半径

addColorStop - 添加色标

代码示例

gradient.addColorStop(offset, color);
参数 说明
offset 0~1 之间的偏移量
color CSS 颜色值

使用渐变

代码示例

// 作为填充样式
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>

五、浏览器兼容性

方法/属性 Chrome Firefox Safari Edge IE
createLinearGradient 1+ 1.5+ 2+ 12+ 9+
createRadialGradient 1+ 1.5+ 2+ 12+ 9+
addColorStop 1+ 1.5+ 2+ 12+ 9+

六、注意事项与最佳实践

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'); // IndexSizeError

3. 颜色格式统一

代码示例

// 推荐:使用标准格式
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 图案的使用。

标签: Canvas渐变 线性渐变 径向渐变 addColorStop 色标 渐变文字

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: 多媒体:绘制文本 - 完整教程与代码示例 下一篇: 多媒体:图案 - 完整教程与代码示例

poll相关推荐