HTML Canvas: How to draw a flipped/mirrored image?
Asked Answered
B

4

46

I'm trying to flip/mirror an image as I paint it on an HTML canvas; I found a game tutorial showing a sprite sheet per direction a character has to face, but this doesn't seem quite right to me. Especially since each frame has a different size.

What would be the best technique to reach this goal?

I tried to call the setScale(-1, 1); on my canvas with no success. Maybe that isn't meant for this.

Thanks

Bindweed answered 17/11, 2011 at 13:52 Comment(2)
Duplicate of How to flip images horizontally with HTML5; see also Rotating a single image but not the others on an HTML5 Canvas.Nonary
Possible duplicate of Flip a sprite in canvasRhenium
N
40
  1. You can do this by transforming the context with myContext.scale(-1,1) before drawing your image, however

  2. This is going to slow down your game. It's a better idea to have a separate, reversed sprite.

Nonary answered 17/11, 2011 at 13:57 Comment(6)
Ok, I was just worried about the memory usage, but cpu availability might be even more sparse on mobile devices. I'll go the way you suggested. Thanks for this advice!Bindweed
Well, don't just take my word for it. Profile it and be sure I'm not wrong about your particular usage. :)Nonary
Agreed, just creating an image sprite with two separate sides is insanely faster.Malti
i just remember, many gba games' sprite sheet only has a half of the object, and it draws the original part and the other part flipped to form a symmetric object, like a tree, or street light. just saying...Chrystel
It's always worthwhile to think about the context of an optimization. In the case of game boy or game boy advance games, developers were extremely constrained by the amount of memory and storage they had. Today, you're unlikely to run into an issue with size. You can still make it a priority, though. If you want to do it as an exercise, for example, why not create a tiny sprite sheet that only has the tightly packed essentials, then write versions of sprites facing different directions into memory before starting the game. You get the speed and memory wins at the cost of load speeds.Pessary
Actually it doesn't decrease performance at all. The 2D canvas context uses a transformation matrix to apply things like scaling to rendered vertexes. With or without a scale call, the transformation matrix is active, so applying a scale isn't going to change the speed things are rendered.Fahy
G
32

You need to set the scale of the canvas as well as inverting the width.

function drawToCanvas(img, context, width, height){
    context.save();
    context.scale(-1, 1);
    context.drawImage(img, 0, 0, width*-1, height);
    context.restore();
}

There are probably some performance issues with this but for me that was not an issue.

Gates answered 17/6, 2014 at 10:3 Comment(5)
Note that variable by canvas should be a context gotten by the call, canvasElem.getContext("2d")Philosophy
"...as well as inverting the width." that is what I was missing ...Starling
works but not on IOSExample
What is parameter v?Skirl
@Envayo "v" is the image to drawWeariless
M
17

If you just flip it horizontally using ctx.scale(-1, 1) it will get off of bounds... so use translate to adjust its position:

ctx.translate(canvas.width, 0);
ctx.scale(-1, 1);
ctx.drawImage(img, 0, 0);

For a shorter code you can remove the translate and use the image size as negative offset in the second parameter of the drawImage (x coordinate) instead:

ctx.scale(-1, 1);
ctx.drawImage(img, canvas.width * -1, 0);

If you want to restore the context later, add save/restore before and after it all:

ctx.save();
ctx.scale(-1, 1);
ctx.drawImage(img, canvas.width * -1, 0);
ctx.restore();
Mithras answered 23/7, 2019 at 19:42 Comment(3)
simplest methodDoran
how to restore context ? its inverted every alternate timeDameron
Use context.save() before and context.restore() after, in order to recover the previous context state.Mithras
O
4

You don't need to redraw the entire image when creating a reflection. An original reflection simply shows the bottom part of the image. This way you are redrawing a smaller part of the image which provides better performance and also you don't need to create linear gradient to hide the lower part of the image (since you never draw it).

 var img = new Image();
 img.src = "//vignette2.wikia.nocookie.net/tomandjerryfan/images/9/99/Jerry_Mouse.jpg/revision/latest?cb=20110522075610";
 img.onload = function() {
   var thumbWidth = 250;
   var REFLECTION_HEIGHT = 50;
   var c = document.getElementById("output");
   var ctx = c.getContext("2d");
   var x = 1;
   var y = 1;

	//draw the original image
   ctx.drawImage(img, x, y, thumbWidth, thumbWidth);
	ctx.save();
	//translate to a point from where we want to redraw the new image
   ctx.translate(0, y + thumbWidth + REFLECTION_HEIGHT + 10);
   ctx.scale(1, -1);
   ctx.globalAlpha = 0.25;
   
   //redraw only bottom part of the image
   //g.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
   ctx.drawImage(img, 0, img.height - REFLECTION_HEIGHT, img.width, REFLECTION_HEIGHT, x, y, thumbWidth, REFLECTION_HEIGHT);

   // Revert transform and scale
  ctx.restore();

 };
 body {
   background-color: #FFF;
   text-align: center;
   padding-top: 10px;
 }
<canvas id="output" width="500" height="500"></canvas>
Octameter answered 14/12, 2016 at 8:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.