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()
}
边界检查
裁剪区域不能超出源图像边界。
坐标系统
注意源坐标和目标坐标的区别。
性能优化
频繁裁剪同一区域可考虑预裁剪缓存。