{T}

Canvas 样式与效果

本文档详细介绍 Canvas 的样式设置和视觉效果,包括填充、描边、渐变、图案、阴影等内容。

填充与描边

基本属性

属性类型默认值说明
fillStylestring/CanvasGradient/CanvasPattern'black'设置填充样式
strokeStylestring/CanvasGradient/CanvasPattern'black'设置描边样式

颜色格式

Canvas 支持多种颜色格式:

javascript
// 颜色名称
ctx.fillStyle = 'red'
ctx.fillStyle = 'blue'

// 十六进制
ctx.fillStyle = '#FF0000'
ctx.fillStyle = '#F00'

// RGB/RGBA
ctx.fillStyle = 'rgb(255, 0, 0)'
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'

// HSL/HSLA
ctx.fillStyle = 'hsl(0, 100%, 50%)'
ctx.fillStyle = 'hsla(0, 100%, 50%, 0.5)'

基本用法

javascript
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')

// 填充样式
ctx.fillStyle = 'red'
ctx.fillRect(10, 10, 100, 80)

// 描边样式
ctx.strokeStyle = 'blue'
ctx.lineWidth = 3
ctx.strokeRect(130, 10, 100, 80)

// 渐变填充
const gradient = ctx.createLinearGradient(10, 110, 210, 110)
gradient.addColorStop(0, 'red')
gradient.addColorStop(1, 'blue')
ctx.fillStyle = gradient
ctx.fillRect(10, 110, 200, 80)

半透明效果

javascript
// 半透明填充
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'
ctx.fillRect(50, 50, 150, 100)

// 叠加效果
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)'
ctx.fillRect(100, 100, 150, 100)

渐变效果

Canvas 支持两种渐变类型:线性渐变和径向渐变。

线性渐变 (createLinearGradient)

方法签名

javascript
const gradient = ctx.createLinearGradient(x0, y0, x1, y1)
gradient.addColorStop(offset, color)

参数说明

参数类型说明
x0Number渐变起点 X 坐标
y0Number渐变起点 Y 坐标
x1Number渐变终点 X 坐标
y1Number渐变终点 Y 坐标
offsetNumber颜色位置(0-1)
colorString颜色值

渐变方向

code
水平渐变:(0, y) → (width, y)
垂直渐变:(x, 0) → (x, height)
对角渐变:(0, 0) → (width, height)

基本用法

javascript
// 水平渐变
const gradient1 = ctx.createLinearGradient(0, 0, 200, 0)
gradient1.addColorStop(0, 'red')
gradient1.addColorStop(0.5, 'yellow')
gradient1.addColorStop(1, 'green')

ctx.fillStyle = gradient1
ctx.fillRect(10, 10, 200, 80)

// 垂直渐变
const gradient2 = ctx.createLinearGradient(0, 100, 0, 180)
gradient2.addColorStop(0, '#3498db')
gradient2.addColorStop(1, '#2ecc71')

ctx.fillStyle = gradient2
ctx.fillRect(10, 100, 200, 80)

// 对角渐变
const gradient3 = ctx.createLinearGradient(0, 200, 200, 280)
gradient3.addColorStop(0, '#e74c3c')
gradient3.addColorStop(0.5, '#f39c12')
gradient3.addColorStop(1, '#9b59b6')

ctx.fillStyle = gradient3
ctx.fillRect(10, 200, 200, 80)

多色渐变

javascript
const rainbow = ctx.createLinearGradient(0, 0, 300, 0)
rainbow.addColorStop(0, 'red')
rainbow.addColorStop(0.17, 'orange')
rainbow.addColorStop(0.33, 'yellow')
rainbow.addColorStop(0.5, 'green')
rainbow.addColorStop(0.67, 'blue')
rainbow.addColorStop(0.83, 'indigo')
rainbow.addColorStop(1, 'violet')

ctx.fillStyle = rainbow
ctx.fillRect(50, 100, 300, 50)

径向渐变 (createRadialGradient)

方法签名

javascript
const gradient = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1)
gradient.addColorStop(offset, color)

参数说明

参数类型说明
x0Number内圆中心 X 坐标
y0Number内圆中心 Y 坐标
r0Number内圆半径
x1Number外圆中心 X 坐标
y1Number外圆中心 Y 坐标
r1Number外圆半径

基本用法

javascript
// 同心圆径向渐变
const radialGradient1 = ctx.createRadialGradient(100, 100, 10, 100, 100, 80)
radialGradient1.addColorStop(0, 'white')
radialGradient1.addColorStop(0.5, 'yellow')
radialGradient1.addColorStop(1, 'red')

ctx.fillStyle = radialGradient1
ctx.beginPath()
ctx.arc(100, 100, 80, 0, Math.PI * 2)
ctx.fill()

// 偏移圆心渐变
const radialGradient2 = ctx.createRadialGradient(80, 80, 10, 100, 100, 80)
radialGradient2.addColorStop(0, 'white')
radialGradient2.addColorStop(0.7, 'cyan')
radialGradient2.addColorStop(1, 'blue')

ctx.fillStyle = radialGradient2
ctx.beginPath()
ctx.arc(250, 100, 80, 0, Math.PI * 2)
ctx.fill()

3D 球体效果

javascript
function drawSphere(x, y, radius, baseColor) {
  // 渐变位置偏移,模拟光源从左上方照射
  const offsetX = radius * 0.3
  const offsetY = radius * 0.3
  
  const gradient = ctx.createRadialGradient(
    x - offsetX, y - offsetY, radius * 0.1,  // 内圆(高光)
    x, y, radius                              // 外圆
  )
  
  gradient.addColorStop(0, 'white')
  gradient.addColorStop(0.3, baseColor)
  gradient.addColorStop(1, 'black')
  
  ctx.beginPath()
  ctx.arc(x, y, radius, 0, Math.PI * 2)
  ctx.fillStyle = gradient
  ctx.fill()
}

drawSphere(100, 100, 50, '#3498db')
drawSphere(220, 100, 50, '#e74c3c')
drawSphere(340, 100, 50, '#2ecc71')

渐变描边

javascript
const gradient = ctx.createLinearGradient(50, 0, 350, 0)
gradient.addColorStop(0, 'red')
gradient.addColorStop(0.5, 'yellow')
gradient.addColorStop(1, 'green')

ctx.strokeStyle = gradient
ctx.lineWidth = 10

ctx.beginPath()
ctx.moveTo(50, 100)
ctx.lineTo(350, 100)
ctx.stroke()

// 渐变描边圆形
ctx.beginPath()
ctx.arc(200, 200, 60, 0, Math.PI * 2)
ctx.stroke()

图案填充

createPattern() 方法使用图像、视频或 Canvas 创建重复图案。

方法签名

javascript
const pattern = ctx.createPattern(image, repetition)

参数说明

参数类型说明
imageHTMLImageElement/HTMLCanvasElement/HTMLVideoElement图案源
repetitionString重复模式

重复模式

模式说明
'repeat'水平和垂直方向重复(默认)
'repeat-x'仅水平方向重复
'repeat-y'仅垂直方向重复
'no-repeat'不重复

使用图像创建图案

javascript
const img = new Image()
img.src = 'texture.png'

img.onload = function() {
  const pattern = ctx.createPattern(img, 'repeat')
  ctx.fillStyle = pattern
  ctx.fillRect(0, 0, 300, 200)
}

使用 Canvas 创建图案

javascript
// 创建图案 Canvas
const patternCanvas = document.createElement('canvas')
patternCanvas.width = 20
patternCanvas.height = 20
const patternCtx = patternCanvas.getContext('2d')

// 在图案 Canvas 上绘制
patternCtx.fillStyle = '#3498db'
patternCtx.fillRect(0, 0, 10, 10)
patternCtx.fillRect(10, 10, 10, 10)

// 创建图案
const pattern = ctx.createPattern(patternCanvas, 'repeat')
ctx.fillStyle = pattern
ctx.fillRect(50, 50, 300, 200)

创建条纹图案

javascript
function createStripePattern(color1, color2, stripeWidth) {
  const patternCanvas = document.createElement('canvas')
  patternCanvas.width = stripeWidth * 2
  patternCanvas.height = stripeWidth * 2
  const pCtx = patternCanvas.getContext('2d')
  
  pCtx.fillStyle = color1
  pCtx.fillRect(0, 0, stripeWidth, stripeWidth)
  pCtx.fillRect(stripeWidth, stripeWidth, stripeWidth, stripeWidth)
  
  pCtx.fillStyle = color2
  pCtx.fillRect(stripeWidth, 0, stripeWidth, stripeWidth)
  pCtx.fillRect(0, stripeWidth, stripeWidth, stripeWidth)
  
  return patternCanvas
}

const stripePattern = createStripePattern('#3498db', '#2ecc71', 10)
const pattern = ctx.createPattern(stripePattern, 'repeat')
ctx.fillStyle = pattern
ctx.fillRect(50, 50, 300, 200)

创建对角线图案

javascript
function createDiagonalPattern(color, gap) {
  const patternCanvas = document.createElement('canvas')
  const size = gap * 2
  patternCanvas.width = size
  patternCanvas.height = size
  const pCtx = patternCanvas.getContext('2d')
  
  pCtx.strokeStyle = color
  pCtx.lineWidth = 2
  
  pCtx.beginPath()
  pCtx.moveTo(0, 0)
  pCtx.lineTo(size, size)
  pCtx.moveTo(size, 0)
  pCtx.lineTo(0, size)
  pCtx.stroke()
  
  return patternCanvas
}

const diagonalPattern = createDiagonalPattern('#e74c3c', 10)
const pattern = ctx.createPattern(diagonalPattern, 'repeat')
ctx.fillStyle = pattern
ctx.fillRect(50, 50, 300, 200)

阴影效果

Canvas 提供了四个属性来设置图形的阴影效果。

阴影属性

属性类型默认值说明
shadowColorString'rgba(0,0,0,0)'阴影颜色
shadowBlurNumber0阴影模糊程度
shadowOffsetXNumber0阴影 X 轴偏移
shadowOffsetYNumber0阴影 Y 轴偏移

基本用法

javascript
// 设置阴影
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'
ctx.shadowBlur = 10
ctx.shadowOffsetX = 5
ctx.shadowOffsetY = 5

// 绘制带阴影的矩形
ctx.fillStyle = '#3498db'
ctx.fillRect(50, 50, 200, 100)

各种阴影效果

javascript
// 柔和阴影
ctx.shadowColor = 'rgba(0, 0, 0, 0.3)'
ctx.shadowBlur = 20
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 10
ctx.fillRect(50, 50, 200, 80)

// 硬边阴影
ctx.shadowColor = 'rgba(0, 0, 0, 0.8)'
ctx.shadowBlur = 0
ctx.shadowOffsetX = 5
ctx.shadowOffsetY = 5
ctx.fillRect(50, 180, 200, 80)

// 发光效果
ctx.shadowColor = '#e74c3c'
ctx.shadowBlur = 30
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0
ctx.fillStyle = '#e74c3c'
ctx.beginPath()
ctx.arc(150, 350, 40, 0, Math.PI * 2)
ctx.fill()

// 多层阴影(需要多次绘制)
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'
ctx.shadowBlur = 10
ctx.shadowOffsetX = 5
ctx.shadowOffsetY = 5
ctx.fillStyle = '#3498db'
ctx.fillRect(300, 50, 150, 80)

ctx.shadowColor = 'rgba(255, 0, 0, 0.3)'
ctx.shadowOffsetX = -5
ctx.shadowOffsetY = -5
ctx.fillRect(300, 50, 150, 80)

文字阴影

javascript
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'
ctx.shadowBlur = 5
ctx.shadowOffsetX = 2
ctx.shadowOffsetY = 2

ctx.font = '40px Arial'
ctx.fillStyle = '#333'
ctx.fillText('带阴影的文字', 50, 100)

// 发光文字
ctx.shadowColor = '#f39c12'
ctx.shadowBlur = 20
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0

ctx.fillStyle = '#f39c12'
ctx.fillText('发光文字', 50, 180)

清除阴影

javascript
// 重置阴影属性
ctx.shadowColor = 'transparent'
ctx.shadowBlur = 0
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0

// 或者保存/恢复状态
ctx.save()
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'
ctx.shadowBlur = 10
// ... 绘制操作 ...
ctx.restore() // 恢复到没有阴影的状态

透明度与合成

globalAlpha

globalAlpha 属性设置所有绘制操作的透明度。

javascript
// 设置全局透明度(0-1)
ctx.globalAlpha = 0.5

// 绘制的所有图形都会有 50% 透明度
ctx.fillStyle = 'red'
ctx.fillRect(50, 50, 150, 100)

ctx.fillStyle = 'blue'
ctx.fillRect(100, 100, 150, 100)

globalCompositeOperation

globalCompositeOperation 决定新绘制的图形如何与已有内容混合。

常用合成模式

模式说明
source-over默认,新图形绘制在已有内容之上
source-in只显示新图形与已有内容重叠的部分
source-out只显示新图形不与已有内容重叠的部分
source-atop新图形只在已有内容上显示
destination-over新图形绘制在已有内容之下
destination-in只显示已有内容与新图形重叠的部分
destination-out只显示已有内容不与新图形重叠的部分
destination-atop已有内容只在新图形上显示
lighter颜色值相加
xor异或操作
multiply正片叠底
screen滤色
overlay叠加

使用示例

javascript
// 基础图形
ctx.fillStyle = '#3498db'
ctx.fillRect(50, 50, 150, 150)

// 使用合成模式
ctx.globalCompositeOperation = 'multiply'
ctx.fillStyle = '#e74c3c'
ctx.beginPath()
ctx.arc(175, 175, 75, 0, Math.PI * 2)
ctx.fill()

// 重置合成模式
ctx.globalCompositeOperation = 'source-over'

橡皮擦效果

javascript
// 绘制背景
ctx.fillStyle = '#3498db'
ctx.fillRect(50, 50, 200, 150)

// 擦除部分内容
ctx.globalCompositeOperation = 'destination-out'
ctx.beginPath()
ctx.arc(150, 125, 50, 0, Math.PI * 2)
ctx.fill()

// 重置
ctx.globalCompositeOperation = 'source-over'

综合示例

渐变按钮

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>
</head>
<body>
  <canvas id="buttonCanvas" width="400" height="300"></canvas>

  <script>
    const canvas = document.getElementById('buttonCanvas')
    const ctx = canvas.getContext('2d')

    function drawGradientButton(x, y, width, height, text) {
      // 阴影
      ctx.shadowColor = 'rgba(0, 0, 0, 0.3)'
      ctx.shadowBlur = 10
      ctx.shadowOffsetX = 0
      ctx.shadowOffsetY = 4

      // 渐变背景
      const gradient = ctx.createLinearGradient(x, y, x, y + height)
      gradient.addColorStop(0, '#667eea')
      gradient.addColorStop(1, '#764ba2')

      ctx.fillStyle = gradient
      
      // 绘制圆角矩形
      const radius = 10
      ctx.beginPath()
      ctx.moveTo(x + radius, y)
      ctx.arcTo(x + width, y, x + width, y + height, radius)
      ctx.arcTo(x + width, y + height, x, y + height, radius)
      ctx.arcTo(x, y + height, x, y, radius)
      ctx.arcTo(x, y, x + width, y, radius)
      ctx.closePath()
      ctx.fill()

      // 清除阴影绘制文字
      ctx.shadowColor = 'transparent'
      
      ctx.fillStyle = 'white'
      ctx.font = 'bold 16px Arial'
      ctx.textAlign = 'center'
      ctx.textBaseline = 'middle'
      ctx.fillText(text, x + width / 2, y + height / 2)
    }

    drawGradientButton(100, 100, 200, 50, '点击按钮')
  </script>
</body>
</html>

艺术效果

javascript
// 大理石纹理
function drawMarbleTexture() {
  const gradient = ctx.createLinearGradient(0, 0, 400, 300)
  gradient.addColorStop(0, '#f5f5f5')
  gradient.addColorStop(0.3, '#e0e0e0')
  gradient.addColorStop(0.5, '#f5f5f5')
  gradient.addColorStop(0.7, '#d0d0d0')
  gradient.addColorStop(1, '#f5f5f5')

  ctx.fillStyle = gradient
  ctx.fillRect(0, 0, 400, 300)

  // 添加纹理线条
  ctx.strokeStyle = 'rgba(0, 0, 0, 0.1)'
  ctx.lineWidth = 1

  for (let i = 0; i < 50; i++) {
    ctx.beginPath()
    ctx.moveTo(Math.random() * 400, Math.random() * 300)
    ctx.bezierCurveTo(
      Math.random() * 400, Math.random() * 300,
      Math.random() * 400, Math.random() * 300,
      Math.random() * 400, Math.random() * 300
    )
    ctx.stroke()
  }
}

// 金属质感
function drawMetallicEffect(x, y, width, height) {
  const gradient = ctx.createLinearGradient(x, y, x + width, y)
  gradient.addColorStop(0, '#8e9eab')
  gradient.addColorStop(0.3, '#eef2f3')
  gradient.addColorStop(0.5, '#8e9eab')
  gradient.addColorStop(0.7, '#eef2f3')
  gradient.addColorStop(1, '#8e9eab')

  ctx.fillStyle = gradient
  ctx.fillRect(x, y, width, height)
}

常见问题

1. 渐变颜色不显示

问题: 设置了渐变但图形没有显示颜色。

解决方案: 确保渐变的起止点在图形范围内。

javascript
// ❌ 错误:渐变范围太小
const gradient = ctx.createLinearGradient(0, 0, 50, 0)
gradient.addColorStop(0, 'red')
gradient.addColorStop(1, 'blue')
ctx.fillStyle = gradient
ctx.fillRect(100, 0, 200, 100) // 渐变只在前 50px

// ✅ 正确:渐变范围匹配图形
const gradient = ctx.createLinearGradient(100, 0, 300, 0)
gradient.addColorStop(0, 'red')
gradient.addColorStop(1, 'blue')
ctx.fillStyle = gradient
ctx.fillRect(100, 0, 200, 100)

2. 阴影影响性能

问题: 大量阴影效果导致性能下降。

解决方案: 缓存带阴影的图形或减少阴影模糊值。

javascript
// 使用离屏 Canvas 缓存带阴影的图形
const offscreen = document.createElement('canvas')
const offCtx = offscreen.getContext('2d')
offscreen.width = 200
offscreen.height = 100

// 在离屏 Canvas 上绘制带阴影的图形
offCtx.shadowColor = 'rgba(0, 0, 0, 0.5)'
offCtx.shadowBlur = 10
offCtx.fillStyle = '#3498db'
offCtx.fillRect(10, 10, 180, 80)

// 在主 Canvas 上重复使用
ctx.drawImage(offscreen, 50, 50)
ctx.drawImage(offscreen, 50, 180)

3. 图案加载时机

问题: 图案不显示或显示错误。

解决方案: 确保在图像加载完成后再创建图案。

javascript
const img = new Image()
img.src = 'pattern.png'

// ✅ 正确:在 onload 中创建图案
img.onload = function() {
  const pattern = ctx.createPattern(img, 'repeat')
  ctx.fillStyle = pattern
  ctx.fillRect(0, 0, 300, 200)
}

下一步学习


返回Canvas 教程目录 | 上一篇路径与曲线