图像裁剪

Canvas图像裁剪详解,掌握区域裁剪、精灵图裁剪、头像裁剪等实用技巧。使用drawImage的9参数形式可以从源图像中裁剪指定区域进行绘制。

裁剪语法

ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
参数说明
sx, sy源图像裁剪起点
sWidth, sHeight源图像裁剪尺寸
dx, dy目标位置
dWidth, dHeight目标尺寸

裁剪原理示意

源图像                    目标画布
+--------+               +--------+
|   sx   |               |   dx   |
|   sy   |               |   dy   |
|  +--+  |  --------->   |  +--+  |
|  |##|  |  裁剪区域     |  |##|  |
|  +--+  |               |  +--+  |
+--------+               +--------+

基本裁剪

const img = new Image()
img.onload = function() {
  // 从(50, 50)裁剪100x100区域,绘制到(0, 0)
  ctx.drawImage(img, 50, 50, 100, 100, 0, 0, 100, 100)
}
img.src = 'image.png'

裁剪演示

图像裁剪示例

精灵图裁剪

精灵图(Sprite Sheet)是游戏开发中常用的技术:

const sprite = {
  image: img,
  frameWidth: 32,
  frameHeight: 32,
  frames: 8
}

function drawFrame(sprite, frameIndex, x, y) {
  const col = frameIndex % 4
  const row = Math.floor(frameIndex / 4)
  
  ctx.drawImage(
    sprite.image,
    col * sprite.frameWidth,
    row * sprite.frameHeight,
    sprite.frameWidth,
    sprite.frameHeight,
    x, y,
    sprite.frameWidth,
    sprite.frameHeight
  )
}

精灵图演示

精灵图动画

九宫格裁剪

用于可拉伸的UI元素:

function draw9Patch(img, x, y, width, height, border) {
  const srcW = img.width
  const srcH = img.height
  const b = border
  
  // 四个角
  ctx.drawImage(img, 0, 0, b, b, x, y, b, b)
  ctx.drawImage(img, srcW - b, 0, b, b, x + width - b, y, b, b)
  ctx.drawImage(img, 0, srcH - b, b, b, x, y + height - b, b, b)
  ctx.drawImage(img, srcW - b, srcH - b, b, b, x + width - b, y + height - b, b, b)
  
  // 四条边
  ctx.drawImage(img, b, 0, srcW - 2 * b, b, x + b, y, width - 2 * b, b)
  ctx.drawImage(img, b, srcH - b, srcW - 2 * b, b, x + b, y + height - b, width - 2 * b, b)
  ctx.drawImage(img, 0, b, b, srcH - 2 * b, x, y + b, b, height - 2 * b)
  ctx.drawImage(img, srcW - b, b, b, srcH - 2 * b, x + width - b, y + b, b, height - 2 * b)
  
  // 中间
  ctx.drawImage(img, b, b, srcW - 2 * b, srcH - 2 * b, x + b, y + b, width - 2 * b, height - 2 * b)
}

九宫格演示

九宫格拉伸

圆形裁剪

使用clip实现圆形头像效果:

function drawCircularImage(img, x, y, radius) {
  ctx.save()
  ctx.beginPath()
  ctx.arc(x + radius, y + radius, radius, 0, Math.PI * 2)
  ctx.clip()
  ctx.drawImage(img, x, y, radius * 2, radius * 2)
  ctx.restore()
}

圆形裁剪演示

圆形头像裁剪

自定义形状裁剪

function drawShapeImage(img, x, y, size, sides) {
  ctx.save()
  ctx.beginPath()
  for (let i = 0; i < sides; i++) {
    const angle = (i * 2 * Math.PI / sides) - Math.PI / 2
    const px = x + size + Math.cos(angle) * size
    const py = y + size + Math.sin(angle) * size
    if (i === 0) ctx.moveTo(px, py)
    else ctx.lineTo(px, py)
  }
  ctx.closePath()
  ctx.clip()
  ctx.drawImage(img, x, y, size * 2, size * 2)
  ctx.restore()
}

注意事项

边界检查

裁剪区域不能超出源图像边界。

坐标系统

注意源坐标和目标坐标的区别。

性能优化

频繁裁剪同一区域可考虑预裁剪缓存。