So I was having this issue with my game; I had cells for up, down, and left animations, but no right cells. So I needed to flip the left cells to draw the right cells.
For each sprite I keep track of it's current top and left in the canvas, as well as each cell's top and left in the sprite sheet.
I've seen previous answers showing a simple horizontal flip as just translating the origin and flipping (inverse scale) of the axes, BUT this does not take into account that with sprites, flipping the origin will mess up the sprite's registration point (its top and left on the canvas).
This issue manifested in the sprite being mirrored correctly, but it's position being off by the width of the sprite. I solved it by taking into account the width of the sprite. Notice how I'm using CanvasRenderingContext2D.prototype.drawImage with 9 arguments since I'm slicing a sprite out of a sprite sheet:
// check if we need to flip image (special case for right movement)
if(sprite.translated){
context.save();
context.translate(context.canvas.width, 0);
context.scale(-1, 1);
context.drawImage(sprite.sheet,
cell.left,
cell.top,
sprite.width,
sprite.height,
// secret sauce: change the destination's X registration point
context.canvas.width - sprite.left - sprite.width,
sprite.top,
sprite.width, sprite.height);
context.restore();
} else {
// Assumes cells are all the same width and height, set in sprite
context.drawImage(sprite.sheet, cell.left, cell.top, sprite.width,
sprite.height, sprite.left, sprite.top, sprite.width, sprite.height);
}
Note: I also could have done the math in the translate, since it's meant to simplify calculations elsewhere.