みなさん、こんにちは。どんぶラッコです。
今日は、canvas要素にvideoタグの中身を表示させるサンプルについて書いていこうと思います!
やりたいこととしては↓↓こういうことです。

ちょっとわかりづらいですが、画面上側が <canvas>
要素です。そこに 画面下部の <video>
要素で読み込んでいるビデオの内容をcanvasタグに反映させています。
では早速 コンポーネントのサンプルとその中身の解説をしていきます!
コンポーネントのサンプル
<script setup lang="ts">
import {
ref,
reactive,
onMounted,
} from 'vue';
// refする要素の準備
const canvas = ref<HTMLCanvasElement>();
const video = ref<HTMLVideoElement>();
const canvasSize = reactive({
width: 640,
height: 480,
});
// 実際に描画をする画面
const drawCanvas = () => {
if (canvas.value === undefined || video.value === undefined) return;
// レンダリング対象のcanvas要素コンテキストを取得
const context:CanvasRenderingContext2D|null = canvas.value.getContext('2d');
if (context === null) return;
context.drawImage(
video.value,
0, 0,
canvasSize.width, canvasSize.height,
);
requestAnimationFrame(drawCanvas);
};
// Events
onMounted(() => {
drawCanvas();
});
</script>
<template>
<div>
<p>canvas</p>
<canvas
ref="canvas"
:width="canvasSize.width"
:height="canvasSize.height"
/>
</div>
<div>
<p>video</p>
<video
ref="video"
src="mock/sample2.mp4"
autoplay
controls
width="400"
/>
</div>
</template>
ではまずテンプレートの部分から。
<template>
<div>
<p>canvas</p>
<canvas
ref="canvas"
:width="canvasSize.width"
:height="canvasSize.height"
/>
</div>
<div>
<p>video</p>
<video
ref="video"
src="mock/sample2.mp4"
autoplay
controls
width="400"
/>
</div>
</template>
canvas
要素と video
要素をそれぞれ準備しています。そして、それぞれのHTML要素には ref
要素を付与しています。これは Vue.js側でその要素自体を指定するための記述ですね。 id
や class
と同じような働きです。
そして、JS側です。
// refする要素の準備
const canvas = ref<HTMLCanvasElement>();
const video = ref<HTMLVideoElement>();
const canvasSize = reactive({
width: 640,
height: 480,
});
先ほど HTML要素それぞれにref
で指定した変数名を宣言します。これらの要素はレンダリング後にDOM要素の実態が紐づきます。なので、現時点では undefined
ですね。
そして、canvasのサイズを変数で格納できるようにしています。こちらはオブジェクト型なので reactive
で宣言。
そして、drawCanvas()
です。
// 実際に描画をする画面
const drawCanvas = () => {
if (canvas.value === undefined || video.value === undefined) return;
// レンダリング対象のcanvas要素コンテキストを取得
const context:CanvasRenderingContext2D|null = canvas.value.getContext('2d');
if (context === null) return;
context.drawImage(
video.value,
0, 0,
canvasSize.width, canvasSize.height,
);
requestAnimationFrame(drawCanvas);
};
if (canvas.value === undefined || video.value === undefined) return;
// 中略
if (context === null) return;
ここら辺は、TSのリントエラーを防ぐために入れている処理です。canvas, video は undefinedの可能性があるので弾いています。また、context
については仕様上 null許容になっているので、nullだった場合は以下の処理を実行しないようにしています。
では、実際のコード画面です。
といっても描画に必要なのはこの3行だけです。
const context:CanvasRenderingContext2D|null = canvas.value.getContext('2d');
context.drawImage(
video.value,
0, 0,
canvasSize.width, canvasSize.height,
);
requestAnimationFrame(drawCanvas);
getContext(contextName)
でどの形式で描画するのかというルールを定義することができます。今回は2d画面でいいので2dと指定します。
そして、 drawImage(要素, X, Y, 幅, 高さ)
で画像や動画を描画させることができます。今回描画したいのは video
要素なので video.value
として要素を取り出しています。
最後に requestAnimationFrame
で、繰り返し実行を定義している、というわけです。
そして、最後に作り上げた drawCanvas()
を 実行するのは DOM要素がマウントされた後にしてもらいたいので、onMounted()
メソッドの中で呼び出します。
// Events
onMounted(() => {
drawCanvas();
});
なぜマウント後に呼び出したいかというと、 canvas
要素、そして video
要素はマウント後にDOM要素として ref
に入ってくるから、でしたね!
噛み砕くと意外と単純ですよね♪
前回記事にした画面キャプチャ機能と組み合わせると、画面キャプチャした要素に対して文字を合成する…なんてこともできそうですね♪
ぜひ試してみてください!