1

所以,这就是问题所在。我在我的反应项目中使用音频 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 为了真正理解请使用安卓手机

4

1 回答 1

1

Reducer 应该是纯函数,在 reducer 之前使用 action 用于 dispatch 和其他人员:

动作.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,
      });
    });
}

减速器.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 回答