When we want to play a video either from video files or video streams, we usually use video
tag in HTML. It's easy to use but no mirroring function is provided. If we want this, we need to implement it by ourself. Below let's discuss a few possible options.
CSS
Transform directive in CSS provides scale
function, we can use this with the video
element. The usage is simple, if we want mirroring horizotally, just use transform: scaleX(-1);
. The following demonstrates the effect corresponding to the different parameters.
As you can see, the purpose is achieved and the performance is good. But if we use controls
with the video
element, this will mirroring too, which is not good.
The last thing we need to know about this approach is old vendor prefixes.
-webkit-transform: scaleX(-1);
-moz-transform: scaleX(-1);
-o-transform: scaleX(-1);
transform: scaleX(-1);
Canvas
Canvas also provides a scale
function, we can use this to implement mirroring too.
<body>
<button id="start">start</button>
<div class="container">
<div class="item">
<video src="./moive.mp4" class="v-1" id="origin-v"></video>
</div>
<div class="item">
<canvas width="480" height="320" id="canvas"></canvas>
</div>
</div>
<script>
const startBtn = document.getElementById("start")
const video = document.getElementById("origin-v")
const canvas = document.getElementById("canvas")
const ctx = canvas.getContext("2d")
// translate first, and then scale
ctx.translate(480, 0)
ctx.scale(-1, 1)
let ratio;
startBtn.onclick = () => {
video.play();
ratio = video.videoWidth / 480;
draw()
}
function draw() {
ctx.drawImage(video, 0, 0, 480, video.videoHeight / ratio);
requestAnimationFrame(draw);
}
</script>
</body>
And the result looks the same.
But we should know that canvas
takes more cpu than plain video
tag, so that's kind of a downside.
More on canvas
Since we can get every pixel value using getImageData
function in canvas, so we can achieve mirroring by manipulating raw pixel value.
<body>
<button id="start">start</button>
<div class="container">
<div class="item">
<video src="./moive.mp4" class="v-1" id="origin-v"></video>
</div>
<div class="item">
<canvas width="480" height="320" id="canvas"></canvas>
</div>
</div>
<script>
const startBtn = document.getElementById("start")
const video = document.getElementById("origin-v")
const canvas = document.getElementById("canvas")
const ctx = canvas.getContext("2d")
const offScreenCanvas = new OffscreenCanvas(480, 320);
const offScreenCtx = offScreenCanvas.getContext("2d");
let ratio;
startBtn.onclick = () => {
video.play();
ratio = video.videoWidth / 480;
draw()
}
function draw() {
const width = 480;
const height = video.videoHeight / ratio;
offScreenCtx.drawImage(video, 0, 0, width, height);
// off screen canvas is used to get image data
const imageData = offScreenCtx.getImageData(0, 0, width, height);
// manipulate image data here to make mirroring effect
const halfWidth = parseInt(imageData.width / 2);
for (let i = 0; i < imageData.height; i++) {
for (let j = 0; j < halfWidth; j++) {
for (let k = 0; k < 4; k++) {
const sourceIdx = i * imageData.width * 4 + (j * 4) + k;
const targetIdx = i * imageData.width * 4 + (imageData.width * 4 - j * 4 - 4) + k;
const temp = imageData.data[targetIdx];
imageData.data[targetIdx] = imageData.data[sourceIdx];
imageData.data[sourceIdx] = temp;
}
}
}
// draw new image data
ctx.putImageData(imageData, 0, 0)
requestAnimationFrame(draw);
}
</script>
</body>
We achieve the same result, but with more cpu burden.