1

それで、ここに問題があります。反応プロジェクトで audio html5 要素を使用しています。問題の典型的な流れは次のとおりです。曲を再生しています。ユーザーは一時停止を押します (時間が 1.20 であるとしましょう)。ユーザーが電話をロックします。数分後、ユーザーが携帯電話のロックを解除し、「再生」ボタンを押すと、次のようになります。 mediaPositionState は、現在の時刻だけをカウントするのではなく、現在の時刻をオーディオの現在の時刻 (1.20) に加えてカウントします。この余分な 1.20 は、曲を変更しても残ります。以下のuseEffectでそれを制御しようとしました

useEffect(() => {
    const audioEl = audioRef.current;
    if (audioEl) {
      audioEl.addEventListener('timeupdate', updateTime);
      audioEl.addEventListener('loadeddata', updatePositionState);
    }
    return () => {
      if (audioEl) {
        audioEl.removeEventListener('timeupdate', updateTime);
        audioEl.removeEventListener('loadeddata', updateTime);
        updatePositionState();
      }
    };
  }, []);

ただし、ユーザーがオーディオに焦点を合わせている場合にのみ正常に機能します。

また、次のコードがあります。

  function updatePositionState() {
    if (navigator.mediaSession?.setPositionState) {
      navigator.mediaSession.setPositionState({
        duration: audioRef.current?.duration ?? 0.0,
        position: audioRef.current?.currentTime ?? 0.0,
      });
    }
  }
  const createMediaSession = (state: AudioStateType) => {
    if (navigator.mediaSession) {
      navigator.mediaSession.metadata = new MediaMetadata({
        title: state.currentSongName,
        artist: state.currentArtistName,
        album: state.currentAlbumName,
        artwork: [
          {
            sizes: '300x300',
            src: `http://storage.musicstream.app/cover/${state.currentAlbumCoverId}`,
          },
        ],
      });
      navigator.mediaSession.setActionHandler('play', function () {
        dispatch({ type: 'resume' });
        updatePositionState();
      });
      navigator.mediaSession.setActionHandler('pause', function () {
        dispatch({ type: 'pause' });
        updatePositionState();
      });
      navigator.mediaSession.setActionHandler('seekto', function (details) {
        dispatch({ type: 'manual_update_time', time: details.seekTime });
        updatePositionState();
      });
      navigator.mediaSession.setActionHandler('previoustrack', () => {
        return 0;
      });
      navigator.mediaSession.setActionHandler('nexttrack', () => {
        return 0;
      });
    }
  };

通常、問題を説明する方法がわかりません。ユーザーが MediaSession 通知をスワイプしたときに mediaposition が台無しになると仮定しましょう。ご要望があれば、より多くのコードを提供します。また、スクリーンショットを提供します(問題を強制しようとしたにもかかわらず、同様の問題が発生しました:トラックの終わりとして時間が表示されます)。

現在の曲時間 現在の曲時間

現在の曲の時間は一時停止しても問題ありません 現在の曲の時間は一時停止しても問題ありません

再生時に現在の曲の時間がめちゃくちゃになる 再生時に現在の曲の時間がめちゃくちゃになる

リクエストによるupdateTime関数の追加は、react.Contextの状態を更新するためだけのものです

const updateTime = () => {
    if (audioRef.current) {
      dispatch({ type: 'update_time', time: audioRef.current.currentTime });
    }
  };

また、完全なレデューサーは次のようになります (役に立たないと思います)。

function audioReducer(state: AudioStateType, action: Action): AudioStateType {
    switch (action.type) {
      case 'fetch_and_play': {
        play(action.songData?.currentSongId).then(() => {
          dispatch({
            type: 'play',
            songData: {
              ...action.songData,
              length: audioRef.current?.duration,
            },
          });
        });
        return state;
      }
      case 'play': {
        createMediaSession({ ...state, ...action.songData });
        return { ...state, ...action.songData };
      }
      case 'pause': {
        pause();
        return { ...state, songIsPaused: true };
      }
      case 'resume': {
        resume();
        return { ...state, songIsPaused: false };
      }
      case 'update_time': {
        return { ...state, currentTime: action.time };
      }
      case 'manual_update_time': {
        if (audioRef.current) {
          audioRef.current.currentTime = action.time;
          return { ...state, currentTime: action.time };
        } else {
          return state;
        }
      }
      default: {
        return state;
      }
    }
  }

私の問題を見ることができるコードサンドボックスを作成しました。https://codesandbox.io/s/wizardly-wiles-87qi1?file=/src/App.js 真に理解するには、Android フォンを使用してください

4

1 に答える 1

1

レデューサーは純粋な関数である必要があります。ディスパッチや他のスタッフにはレデューサーの前にアクションを使用してください。

Action.ts

const fetchAndPlay = (currentSongId) => {
   play(currentSongId).then(() => {
        createMediaSession({ ...state, ...action.songData });
        dispatch({
          type: 'play',
          //just pass data u need to show or you want rerender them
          length: audioRef.current?.duration,
      });
    });
}

Reducer.ts

function audioReducer(state: AudioStateType, action: Action): AudioStateType {
switch (action.type) {
  case 'play': 
       return { ...state, length:action.length, playing: true};
  ...
}

あなたのコンポーネントでは、このアクションを呼び出すだけです:

fetchAndPlay(songId)
于 2021-01-03T15:25:45.807 に答える