🔥
映像をAA風にリアルタイムで描画するのを、作った
経緯
作りたかったから
フォルダ構造
.
├── index.html
├── index.js
└── video.mp4
コード
index.html
<!DOCTYPE html>
<html lang="js">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<video id="video" controls src="video.mp4" style="position: absolute; top: 0; left: 0; width: 10%; aspect-ratio: 16/9;"></video>
<canvas id="previewCanvas" style="width: 100%; aspect-ratio: 16/9;"></canvas>
<script src="index.js"></script>
</body>
</html>
index.js
function $(id) {return document.getElementById(id)}
const resolve = 10;
let frameCanvas, preCanvas;
let frameCtx, preCtx;
let width, height;
let offscreen, offCtx;
let charWidth, charHeight;
let lastFrameTime = 0;
// const chars = '■WMBDOQGpqbdwmZXYUJCLftjri|}{][)(><+~-_:;,^`\'. ';
const chars = '■■■■@@@@@%WMBDOQGpqbdwmZXYUJCLftjri#####|}{][)(*+=-:. ';
// const chars = '■'
const FPS = 24;
const frameDuration = 1000/FPS;
function handleVideoFrame(now) {
const timeLastFrame = now - lastFrameTime;
if(timeLastFrame >= frameDuration) {
lastFrameTime = now;
preCtx.clearRect(0, 0, preCanvas.width, preCanvas.height);
offCtx.clearRect(0, 0, width, height);
offCtx.drawImage(this.video, 0, 0, width, height);
let imageData = offCtx.getImageData(0, 0, width, height);
let pixels = imageData.data;
for(let y = 0; y < height; y++) {
for(let x = 0; x < width; x++) {
const base = (y * width + x) * 4;
const [r, g, b] = [pixels[base], pixels[base+1], pixels[base+2]];
const brightness = (r * 0.299 + g * 0.587 + b * 0.114) / 255;
const charIndex = Math.floor(brightness * (chars.length - 1));
const char = chars[charIndex];
preCtx.fillStyle = `rgb(${r}, ${g}, ${b})`;
preCtx.fillText(char, x * charWidth, y * charHeight);
}
}
}
this.video.requestVideoFrameCallback(handleVideoFrame);
}
function init() {
const video = $("video");
preCanvas = $("previewCanvas");
const rect = preCanvas.getBoundingClientRect();
width = Math.floor(rect.width / resolve);
height = Math.floor(rect.height / resolve);
preCanvas.width = rect.width;
preCanvas.height = rect.height;
// 1文字あたりのサイズを計算
charWidth = rect.width / width;
charHeight = rect.height / height;
// コンテキスト設定
preCtx = preCanvas.getContext("2d");
// フォントサイズを文字サイズに合わせて設定
const fontSize = Math.min(charWidth, charHeight) * 1;
preCtx.font = `bold ${Math.floor(fontSize)}px monospace`;
preCtx.textBaseline = 'top';
preCtx.textAlign = 'left';
preCtx.imageSmoothingEnabled = false;
offscreen = new OffscreenCanvas(width, height);
offCtx = offscreen.getContext("2d");
offCtx.imageSmoothingEnabled = false;
video.requestVideoFrameCallback(handleVideoFrame.bind( {video} ));
}
document.addEventListener('DOMContentLoaded', () => {
$("video").addEventListener('loadedmetadata', init);
});
使い方
VSCodeとかのLive Serverで、index.html
を立ち上げて動画を再生開始するだけ
お粗末なコードなので、たまにコンテンツが読み込まれなくて、再生しても描画されないことがあります
Discussion