はじめに
以前、業務でvideo.jsを触っていた時にended
イベントが発火せず苦戦したので、その時に調査した内容と解決策をここにまとめます。
発生していた問題
動画がplayした時間、stopした時間、endedした時間をサーバに返す必要があり、それぞれのイベントリスナーが発火した時間を取得しようとしていましたが、ended
イベントのみが発火しませんでした
原因
video.jsでは、videoのloop再生をONにしているとended
イベントが発火されずにループする挙動になります。業務で用いているvideo.jsでは、loopをONにしていたためにended
イベントが発生しませんでした。
解決策1 loopをOFFにする
loopがONだとended
イベントが発生しないので単純にloopをOFFにすれば解決することはできます。
しかし、業務では諸事情でloop再生をOFFにすることはできなかったので、なんとかして動画の終了イベントを検知する必要がありました。
解決策2 timeupdateイベントから擬似的なendedイベントを発火させる
video.jsのイベントには、動画の現在時刻が更新されたときに発生するtimeupdate
イベントがあります。このtimeupdate
が発火した時のcurrentTime
の値を変数に持っておき、動画の終了時刻から開始時刻に変化したタイミングを擬似的にended
イベントとすることができます。
例(React)
const prevTimeRef = useRef<number>(0); // ループを検知するためにtimeupdateイベントの直前の時間を保持
const onReady = useCallback(
(player: videojs.Player) => {
// ループを検知してendedイベントを発火させる
player.on('timeupdate', () => {
if (
prevTimeRef.current - player.currentTime() >
player.duration() - 1
) {
console.log('ended')
}
prevTimeRef.current = player.currentTime();
});
},
[]
);
ただし、timeupdate
イベントの頻度はシステムの負荷に依存するため、正確な終了時刻を取ることはできないという課題があるため注意が必要です。今回は1秒以内に収まっていればループしたと判断しました。
最後に
loop時にloop
イベントなるものが標準で用意されてくれていればこんな回りくどいことする必要ないんですけどね。。。
今回はloopをOFFにできなかったので苦肉の策でしたしより良い方法もあると思います。もしより良い方法があればコメントで教えてくださるとありがたいです!
ご覧いただきありがとうございました!