1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ReactPlayerでループ時にイベントを発火させる方法

Last updated at Posted at 2023-07-17

はじめに

どうも最近Reactを触って1ヶ月のぴよぴよ🐣です。
今回の記事は2023/07の情報をもとに記載しているため、今後ReactPlayerの対応によっては以下の方法よりもっと良い方法ができるようになるかもしれないことを念頭において読んでいただけると幸いです。

結論

公式のドキュメントにも書いてありますが、ループ終了時にはonEndedは発火しませんので、不可能です。
スクリーンショット 2023-07-17 0.16.51.png

なので、今回は疑似的に発火させる方法をご紹介します。

  1. onDurationで動画全体の秒数を取得する
  2. onProgressで動画の数秒前に行いたい処理を実行する

大雑把に動きやコードが見たい方はこちらからどうぞ

ことの始まり

動画教材を扱うサービスであれば「動画が見終わったか」をDBなどに取得したときは多いでしょう。
これをループした時に行いたいということがあったのがことの始まりです。

Issueを見てみる

既にいくつかループ時にEndedが発火しないというIssueは立っており、そこでもonDurationonProgressを使ってねーと言われています。
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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?