HTML canvas 显示图块集中相邻图像的微小切片
HTML canvas shows tiny slice of adjacent image in tileset
我一直在使用 HTML5 canvas 和 JavaScript 编写游戏,当我尝试旋转图像时,它会显示相邻图像的一小部分精灵 sheet。我知道我可以将精灵中的图像分开 sheet,但我正试图找到另一种解决问题的方法,例如更改设置。
问题不大,就是不指定就抓一张相邻的图就奇怪了。精灵为 16 x 16 像素。
screenshot
another one
in the sprite sheet
绘制手部精灵的代码行是第二个绘制图像,我正在使用索引来抓取图像。此处,结果为 208,也就是图像中绿色方块所在的位置。
c.save();
c.translate(canvas.width/2, canvas.height/2);
if(mouseAngle >= 90 || mouseAngle <= -90) {
c.scale(-1, 1);
c.rotate(Math.PI / 180 * (180 + -mouseAngle));
} else {
c.rotate(Math.PI / 180 * (mouseAngle));
};
c.drawImage(Images.items, itemID[this.heldItem] * 16, 0, 16, 16, scale, -12 * scale, 16 * scale, 16 * scale);
c.drawImage(Images.player, this.handFramePath[this.dmgIndex] * 16, 0, 16, 16, scale, -12 * scale, 16 * scale, 16 * scale);
c.restore();
是的,纹理确实因 drawImage
的裁剪而流血。
通常我们可以通过确保我们的上下文变换在整数坐标上来避免这种情况,以避免任何抗锯齿,但对于旋转......这更复杂。
所以在你的情况下最好的(实际上有或没有流血)是从精灵中提取每个精灵 - sheet 在它自己的 ImageBitmap 对象中。
这样裁剪将在没有任何转换的情况下完成,并且它有一个额外的好处,允许浏览器优化更经常使用的精灵(而不是移动整个精灵-sheet 每一次)。
(async() => {
const spritesheet = document.querySelector("img");
await spritesheet.decode();
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// apply some transforms
ctx.translate(50, 50);
ctx.rotate(Math.PI/32);
ctx.translate(-50, -50);
// draw only the gray rectangle
// cropped from the full spritesheet on the left
// (bleeds in all directions on Chrome)
ctx.drawImage(spritesheet,
8, 8, 8, 8,
50, 50, 50, 50
);
// single sprite on the right
const sprite = await createImageBitmap(spritesheet, 8, 8, 8, 8);
ctx.drawImage(sprite,
150, 50, 50, 50
);
})().catch(console.error);
<p>The original sprite-sheet:<img src="https://i.stack.imgur.com/I1xPN.png"></p>
<canvas></canvas>
createImageBitmap
现在在所有最新的浏览器中都受支持,但对于旧浏览器(例如 Safari 几周前才公开它),我做了一个 polyfill 你可以找到 here。
我一直在使用 HTML5 canvas 和 JavaScript 编写游戏,当我尝试旋转图像时,它会显示相邻图像的一小部分精灵 sheet。我知道我可以将精灵中的图像分开 sheet,但我正试图找到另一种解决问题的方法,例如更改设置。
问题不大,就是不指定就抓一张相邻的图就奇怪了。精灵为 16 x 16 像素。
screenshot
another one
in the sprite sheet
绘制手部精灵的代码行是第二个绘制图像,我正在使用索引来抓取图像。此处,结果为 208,也就是图像中绿色方块所在的位置。
c.save();
c.translate(canvas.width/2, canvas.height/2);
if(mouseAngle >= 90 || mouseAngle <= -90) {
c.scale(-1, 1);
c.rotate(Math.PI / 180 * (180 + -mouseAngle));
} else {
c.rotate(Math.PI / 180 * (mouseAngle));
};
c.drawImage(Images.items, itemID[this.heldItem] * 16, 0, 16, 16, scale, -12 * scale, 16 * scale, 16 * scale);
c.drawImage(Images.player, this.handFramePath[this.dmgIndex] * 16, 0, 16, 16, scale, -12 * scale, 16 * scale, 16 * scale);
c.restore();
是的,纹理确实因 drawImage
的裁剪而流血。
通常我们可以通过确保我们的上下文变换在整数坐标上来避免这种情况,以避免任何抗锯齿,但对于旋转......这更复杂。
所以在你的情况下最好的(实际上有或没有流血)是从精灵中提取每个精灵 - sheet 在它自己的 ImageBitmap 对象中。
这样裁剪将在没有任何转换的情况下完成,并且它有一个额外的好处,允许浏览器优化更经常使用的精灵(而不是移动整个精灵-sheet 每一次)。
(async() => {
const spritesheet = document.querySelector("img");
await spritesheet.decode();
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// apply some transforms
ctx.translate(50, 50);
ctx.rotate(Math.PI/32);
ctx.translate(-50, -50);
// draw only the gray rectangle
// cropped from the full spritesheet on the left
// (bleeds in all directions on Chrome)
ctx.drawImage(spritesheet,
8, 8, 8, 8,
50, 50, 50, 50
);
// single sprite on the right
const sprite = await createImageBitmap(spritesheet, 8, 8, 8, 8);
ctx.drawImage(sprite,
150, 50, 50, 50
);
})().catch(console.error);
<p>The original sprite-sheet:<img src="https://i.stack.imgur.com/I1xPN.png"></p>
<canvas></canvas>
createImageBitmap
现在在所有最新的浏览器中都受支持,但对于旧浏览器(例如 Safari 几周前才公开它),我做了一个 polyfill 你可以找到 here。