Is it possible to mirror an image horizontally (flip) with Canvas?

4

I know it's possible to mirror a <img> horizontally with CSS, but wanted to know if it is possible to mirror an image horizontally within a canvas .

With CSS, I would do so:

#img-flip{
   transform: scaleX(-1);
}
    <img src="https://i.stack.imgur.com/pA0a1.png"id="img">
        <img src="https://i.stack.imgur.com/pA0a1.png"id="img-flip">

And with canvas ? How can I do this?

var onload = function() {
    var canvas = document.querySelector("#canvas");
    var ctx = canvas.getContext("2d");
    var img = document.querySelector("#img");
    canvas.height = img.height;
    canvas.width = img.width;
    ctx.drawImage(img, 0, 0);
}


img.completed ? onload() : img.addEventListener('load', onload);
<div>Imagem original:</div>
<img src="https://i.stack.imgur.com/pA0a1.png"id="img">

<div>Resultado Canvas:</div>
<canvas id="canvas"></canvas>
    
asked by anonymous 03.05.2018 / 21:17

2 answers

3

You can use context.scale(-1, 1); and pass as width of the image times -1 when performing the drawImage() of the context.

var onload = function() {
    var canvas = document.querySelector("#canvas");
    var ctx = canvas.getContext("2d");
    var img = document.querySelector("#img");
    canvas.height = img.height;
    canvas.width = img.width;
    ctx.scale(-1, 1);
    ctx.drawImage(img, 0, 0, img.width*-1, img.height);
}


img.completed ? onload() : img.addEventListener('load', onload);
<div>Imagem original:</div>
<img src="https://i.stack.imgur.com/pA0a1.png"id="img">

<div>Resultado Canvas:</div>
<canvas id="canvas"></canvas>

Reference HTML Canvas: How to draw a flipped / mirrored image

    
03.05.2018 / 21:23
1

An additional suggestion to the already existing answer would be to use context.translate to fit the entire canvas instead of setting the X position in the image, because when using context.scale it affects the entire canvas, (assuming you are going to use more than one) and other added elements that should also be reversed.

Example of difficulty (note that the rect is not in position x = 20):

var onload = function() {
    var canvas = document.querySelector("#canvas");
    var ctx = canvas.getContext("2d");
    var img = document.querySelector("#img");
    canvas.height = img.height;
    canvas.width = img.width;
    ctx.scale(-1, 1);
    ctx.drawImage(img, 0, 0, img.width*-1, img.height);

    ctx.rect(20, 20, 100, 100);
    ctx.stroke();
}

img.completed ? onload() : img.addEventListener('load', onload);
<div>Imagem original:</div>
<img src="https://i.stack.imgur.com/pA0a1.png"id="img">

<div>Resultado Canvas:</div>
<canvas id="canvas"></canvas>
So to "fix" this, so that the position x = 20 in drawImage is not inverted in relation to the canvas size ( drawImage ) we would have to compensate, doing something like:

ctx.rect(-(img.width - 100 - 20), 20, 100, 100);

That is, we have to add the width of the rect to the desired position and transform the value into negative, eg:

var onload = function() {
    var canvas = document.querySelector("#canvas");
    var ctx = canvas.getContext("2d");
    var img = document.querySelector("#img");
    canvas.height = img.height;
    canvas.width = img.width;
    ctx.scale(-1, 1);

    ctx.drawImage(img, 0, 0, -img.width, img.height);
    ctx.rect(-(img.width - 100 - 20), 20, 100, 100);
    ctx.strokeStyle = "red";
    ctx.stroke();
}

img.completed ? onload() : img.addEventListener('load', onload);
<div>Imagem original:</div>
<img src="https://i.stack.imgur.com/pA0a1.png"id="img">

<div>Resultado Canvas:</div>
<canvas id="canvas"></canvas>

Using translate

However this can be tricky, costly even for development time and certainly is something that can be simplified using <canvas width="... , example

var onload = function() {
    var canvas = document.querySelector("#canvas");
    var ctx = canvas.getContext("2d");
    var img = document.querySelector("#img");
    canvas.height = img.height;
    canvas.width = img.width;
    ctx.scale(-1, 1);
    ctx.translate(-canvas.width, 0);
    ctx.drawImage(img, 0, 0, img.width, img.height);

    ctx.rect(20, 20, 100, 100);
    ctx.stroke();
}

img.completed ? onload() : img.addEventListener('load', onload);
<div>Imagem original:</div>
<img src="https://i.stack.imgur.com/pA0a1.png"id="img">

<div>Resultado Canvas:</div>
<canvas id="canvas"></canvas>

Done, this way you do not need to calculate anything, you can apply more than one object or image to the canvas that it will draw like a mirror without needing adjustments, the same can be done with the axis rect if you want to flip vertically it would look like this:

ctx.scale(1, -1);
ctx.translate(0, -canvas.height);
ctx.drawImage(img, 0, 0, img.width, img.height);
    
04.05.2018 / 17:18