今回は、Vue.jsを利用してプレビュー動画を表示するようなコンポーネントを開発していた際にvideoの読み込みが行われないという問題があったので、その問題の対処方法について紹介します。
前提条件
- Nuxt.jsの2.15.3
下記のような動画を読み込んで、読み込んだ動画をプレビュー表示するようなコンポーネントを開発しようと思います。
該当部分のソースコードを示します。
<template>
<div>
<!-- ファイル入力 -->
<input accept="video/mp4" @change="fileSelect" />
<!-- プレビューとして表示 -->
<video controls >
<source :src="src" type="video/mp4">
</video>
</div>
</template>
<script lang="ts">
import {
defineComponent,
ref,
} from '@nuxtjs/composition-api'
export default defineComponent({
setup(_props: any, _context: any) {
const src = ref()
// ファイルを読みんでDataURL(Base64文字列)を返す
const readFileAsDataURL = (file: any) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = (evt: any) => {
resolve(evt.target.result)
}
reader.onerror = reject;
reader.readAsDataURL(file)
})
}
// ファイル選択
const fileSelect = async (_event: any) => {
const file = event.target.files[0]
if (!file || !file.type.match('video/*')) {
return
}
const data = await readFileAsDataURL(file)
// 選択された動画を見えるようにする。
src.value = data
}
return {
src,
fileSelect,
}
},
})
</script>
このとき、動画ファイルを2回読み込むと、読み込んだファイルが更新されません。
対策例
Videoタグは、HTMLMediaElementというエレメントにあたります。ドキュメントを引用すると
HTMLMediaElement
のload()
メソッドは、メディア要素をその初期状態にリセットし、再生を開始する準備としてメディアソースを選択してメディアを読み込むプロセスを開始します。 プリフェッチされるメディアデータの量は、要素のpreload
属性の値によって決まります。
とあります。load()をコールすれ強制的の再生開始準備ができるようなので、srcを更新した後にコールするようにします。
先程のコードを下記のように変更します。
<template>
<div>
<!-- ファイル入力 -->
<input accept="video/mp4" @change="fileSelect" />
<!-- プレビューとして表示 -->
<video controls ref="previewVideo">
<source :src="src" type="video/mp4">
</video>
</div>
</template>
<script lang="ts">
import {
defineComponent,
ref,
} from '@nuxtjs/composition-api'
export default defineComponent({
setup(_props: any, _context: any) {
const src = ref()
const previewVideo = ref()
// ファイルを読みんでDataURL(Base64文字列)を返す
const readFileAsDataURL = (file: any) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = (evt: any) => {
resolve(evt.target.result)
}
reader.onerror = reject;
reader.readAsDataURL(file)
})
}
// ファイル選択
const fileSelect = async (_event: any) => {
const file = event.target.files[0]
if (!file || !file.type.match('video/*')) {
return
}
const data = await readFileAsDataURL(file)
// 選択された動画を見えるようにする。
src.value = data
// 強制的にロードする
previewVideo.load()
}
return {
src,
fileSelect,
previewVideo,
}
},
})
</script>
vue.jsのcomposition apiでは、従来のthis.$refs.hogehoge
という形での
Dom参照する変数をref()を使って、定義することで参照できます。
この対応を入れることで無事問題解決しました。
リアクティブなVue.jsやNuxt.jsに慣れてくるとデータを変えたら、いい感じに画面が切り替わるので、意図的にイベントを拾って更新しないといけないElementもあるようです。