Canvas 样式与效果
本文档详细介绍 Canvas 的样式设置和视觉效果,包括填充、描边、渐变、图案、阴影等内容。
填充与描边
基本属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
fillStyle | string/CanvasGradient/CanvasPattern | 'black' | 设置填充样式 |
strokeStyle | string/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)参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
x0 | Number | 渐变起点 X 坐标 |
y0 | Number | 渐变起点 Y 坐标 |
x1 | Number | 渐变终点 X 坐标 |
y1 | Number | 渐变终点 Y 坐标 |
offset | Number | 颜色位置(0-1) |
color | String | 颜色值 |
渐变方向
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)参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
x0 | Number | 内圆中心 X 坐标 |
y0 | Number | 内圆中心 Y 坐标 |
r0 | Number | 内圆半径 |
x1 | Number | 外圆中心 X 坐标 |
y1 | Number | 外圆中心 Y 坐标 |
r1 | Number | 外圆半径 |
基本用法
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)参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
image | HTMLImageElement/HTMLCanvasElement/HTMLVideoElement | 图案源 |
repetition | String | 重复模式 |
重复模式
| 模式 | 说明 |
|---|---|
'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 提供了四个属性来设置图形的阴影效果。
阴影属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
shadowColor | String | 'rgba(0,0,0,0)' | 阴影颜色 |
shadowBlur | Number | 0 | 阴影模糊程度 |
shadowOffsetX | Number | 0 | 阴影 X 轴偏移 |
shadowOffsetY | Number | 0 | 阴影 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 教程目录 | 上一篇:路径与曲线