はじめに
どうも最近Reactを触って1ヶ月のぴよぴよ🐣です。
今回の記事は2023/07の情報をもとに記載しているため、今後ReactPlayerの対応によっては以下の方法よりもっと良い方法ができるようになるかもしれないことを念頭において読んでいただけると幸いです。
結論
公式のドキュメントにも書いてありますが、ループ終了時にはonEnded
は発火しませんので、不可能です。
なので、今回は疑似的に発火させる方法をご紹介します。
-
onDuration
で動画全体の秒数を取得する -
onProgress
で動画の数秒前に行いたい処理を実行する
大雑把に動きやコードが見たい方はこちらからどうぞ
ことの始まり
動画教材を扱うサービスであれば「動画が見終わったか」をDBなどに取得したときは多いでしょう。
これをループした時に行いたいということがあったのがことの始まりです。
Issueを見てみる
既にいくつかループ時にEndedが発火しないというIssueは立っており、そこでもonDuration
とonProgress
を使ってねーと言われています。
https://github.com/CookPete/react-player/issues/496
https://github.com/cookpete/react-player/issues/471
悪い例:onProgress=0で動かしてみる
0秒目を支点にすれば、onDurationを使わなくて良いじゃん!と思いますが、動画を最初からにするとonProgressが動作します。そのため、「最後まで見た」ということはできません。
const [loopCount, setLoopCount] = React.useState(0)
function handlerOnProgress(state) {
if (state.playedSeconds <= 0) {
setLoopCount(loopCount + 1)
}
}
return (
<div>
<ReactPlayer
url='https://samplelib.com/lib/preview/mp4/sample-5s.mp4'
controls={true}
playing={true}
loop={true}
onEnded={() => console.log('onEnded')} //悲しいかな発火しない子
onProgress={handlerOnProgress}
/>
<h3>
Loop count: {loopCount}
</h3>
</div>
);
良い例:onDurationとonProgressを使う
この場合は「動画の最後」にしっかり発火するので、正常にループの直前に動作してくれます。
ただ、onProgressの実行間隔のデフォルトは10000msなので、progressInterval
を短くするか、1.5秒など少しゆとりのある時間にしましょう。
ただ、この実装だとループする度に発火することになります。
const [loopCount, setLoopCount] = React.useState(0)
const [duration, setDuration] = React.useState(0)
function handlerOnProgress(state) {
if (state.playedSeconds > duration - 1.5) {
setLoopCount(loopCount + 1)
}
}
return (
<div>
<ReactPlayer
url='https://samplelib.com/lib/preview/mp4/sample-5s.mp4'
controls={true}
playing={true}
loop={true}
onEnded={() => console.log('onEnded')} //悲しいかな発火しない子
onDuration={setDuration}
onProgress={handlerOnProgress}
/>
<h3>
Loop count: {loopCount}
</h3>
</div>
);
間違いなく最初のループだけで動作する例
今回私がやりたかったのは、ループの1回目飲みを取得することだったの、以下のような実装にしました。
const [loopCount, setLoopCount] = React.useState(0)
const [duration, setDuration] = React.useState(0)
const [playerVideo, setPlayerVideo] = React.useState(true)
function handlerOnProgress(state) {
if (playerVideo && state.playedSeconds > duration - 1.5) {
setLoopCount(loopCount + 1)
setPlayerVideo(false)
}
}
return (
<div>
<ReactPlayer
url='https://samplelib.com/lib/preview/mp4/sample-5s.mp4'
controls={true}
playing={true}
loop={true}
onEnded={() => console.log('onEnded')} //悲しいかな発火しない子
onDuration={setDuration}
onProgress={handlerOnProgress}
/>
<h3>
Loop count: {loopCount}
</h3>
</div>
);
総括
実際に動画をループするだけなら、Safari以外なら右クリックでループさせることができるので要件がループだけならそれでカバーするのもありだと個人的には思います。
また、今回ChatGPTをペアプロしながら実装しましたが間違ったこともいうので、皆さんも実際に実装するときは自分の目でドキュメントやコードを読むようにしましょう!
実際に言われたこと一覧
わい:onProgressを有効・無効で切り替えることはできますか?
ChatGPT : progressIntervalをnullにすることで可能です
progressIntervalは実際にはnumberしか持たないのでこの方法は使えません。
https://github.com/cookpete/react-player/blob/2811bc59b9368170acc20d4f1e39555413d0d9e1/src/props.js#L16