JSFiddle - React, Tailwind, and code Playground
by Csaba Hellinger
HTML
<video src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4" autoplay muted controls></video>
<div id="container">
</div>
CSS
/* only use border radius if we can apply it during the transition too */
@supports(view-transition-group: contain) {
:root {
--radius: 1.5rem;
}
}
::view-transition-group(container) {
border-radius: var(--radius);
overflow: clip;
}
::view-transition-group(*) {
animation-duration: 400ms;
transition-timing-function: ease-in-out;
overflow: clip;
}
body {
background: #222;
}
video {
width: 500px;
__opacity: 0;
__position: absolute;
__pointer-events: none;
}
#container {
--columns: 2;
display: inline-grid;
grid-template-columns: repeat(var(--columns), 0fr);
gap: 0.2rem;
border-radius: var(--radius);
overflow: clip;
view-transition-name: container;
view-transition-group: contain;
}
canvas {
view-transition-name: match-element;
}
JavaScript
const SPLIT_WIDTH = 4;
const SPLIT_HEIGHT = 3;
const video = document.querySelector('video');
const container = document.querySelector('#container');
container.style = `--columns: ${SPLIT_WIDTH}`;
const blocks = [];
for (let y = 0; y < SPLIT_HEIGHT; y += 1) {
for (let x = 0; x < SPLIT_WIDTH; x += 1) {
const canvas = document.createElement('canvas');
const context = canvas.getContext("2d");
canvas.width = 100;
canvas.height = 100;
container.appendChild(canvas);
blocks.push({ canvas, context, x, y });
}
}
function onFrame() {
const blockWidth = video.videoWidth / SPLIT_WIDTH;
const blockHeight = video.videoHeight / SPLIT_HEIGHT;
const canvasWidth = video.clientWidth / SPLIT_WIDTH;
const canvasHeight = video.clientHeight / SPLIT_HEIGHT;
for ({canvas, context, x, y} of blocks) {
canvas.width = canvasWidth;
canvas.height = canvasHeight;
context.drawImage(
video,
blockWidth * x, blockHeight * y, blockWidth, blockHeight,
0, 0, canvasWidth, canvasHeight
);
}
if (!video.paused && !video.ended) {
video.requestVideoFrameCallback(onFrame);
}
}
const timer = setInterval(() => {
document.startViewTransition(() => {
for ({ canvas } of blocks) {
canvas.style.order = Math.round(Math.random() * 1000);
}
});
}, 1000);
video.onloadeddata = onFrame;
video.onplay = onFrame;
// test init
video.currentTime = 15;
window.stop = () => {
video.pause();
clearInterval(timer);
}