I've been programming a game using the HTML5 canvas and JavaScript, and when I try to rotate an image, it displays a tiny sliver of the adjacent image from the sprite sheet. I know that I could separate the images in the sprite sheet, but I'm trying to find another way to solve the problem, like changing a setting.
It isn't a big problem, but it's strange that a piece of an adjacent image would be grabbed when it was not specified. The sprites are 16 by 16 pixels.
The line of code that draws the hand sprite is the second draw image, and I'm using an index to grab the images. Here, the result is 208, which is where the green square is in the image.
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();
Yes, textures do bleed from the cropping of drawImage.
Usually we can try to prevent that by ensuring that our context's transforms are on integer coordinates, as to avoid any antialiasing, but for rotation... that's more complex.
So the best in your case (with or without bleeding actually), is to extract each sprite from the sprite-sheet in its own ImageBitmap object.
This way the cropping will be done without any transformation messing in, and it will have the added benefit of allowing the browser to optimize the sprites that are used more often (rather than moving the whole sprite-sheet every time).
(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 is now supported in all up to date browsers, but for older ones (e.g Safari did expose it only a few weeks ago), I made a polyfill you can find here.