Vimeo,检测全屏以阻止获取新播放器(动态宽度变化)

Vimeo, detect fullscreen to block fetching of new player (dynamic width changes)

我想动态更改我的 vimeo 播放器的宽度以适应 window 宽度。您可以在问题的末尾看到组件的完整代码,也许已经有一种更简单的方法来动态更改播放器的宽度,因此我根本不必处理这些问题(提供的响应选项vimeo 对我不起作用)。

我当前解决方案遇到的问题:如果播放器进入全屏或在全屏上旋转 phone,我不想触发更改,因为 vimeo 已经自动处理这些更改,但我有很难检测播放器是否处于全屏模式。

    const isFullscreen = (document.fullscreenElement
      || document.webkitFullscreenElement
      || document.mozFullScreenElement
      || document.msFullscreenElement
      || playerWidth === delayedWidth)

此解决方案对 iphone 11 pro 和可能所有 Safari 浏览器无效,因为那里没有完全实现全屏。

import React, {
  useEffect, useContext, useState, useRef,
} from 'react';
import PropTypes from 'prop-types';
import Player from '@vimeo/player';

import STYLES from '../../enums/styles';
import { BrowserContext } from '../../contexts/BrowserContext';

const TAG = 'player';

/**
 * remove event listeners
 * @param {object} playerRef
 */
function removeEventListeners(playerRef) {
  if (!playerRef.current) return;
  playerRef.current.off('ended');
  playerRef.current.off('pause');
  playerRef.current.off('play');
}

/**
 * remove interval
 * @param {object} intervalRef
 */
function removeInterval(intervalRef) {
  if (!intervalRef.current) return;
  window.clearInterval(intervalRef.current);
}

/**
 * 640×480, 800×600, 960×720, 1024×768, 1280×960,
 * 1400×1050, 1440×1080 , 1600×1200, 1856×1392, 1920×1440, and 2048×1536
 * @param {} width
 */
function computeRatio(delayedWidth) {
  const height = window.innerHeight;
  const width = delayedWidth - delayedWidth * 0.1;

  if (height <= 480) {
    return width > 640 ? 640 : width;
  }
  if (height <= 600) {
    return width > 800 ? 800 : width;
  }
  if (height <= 720) {
    return width > 960 ? 960 : width;
  }
  if (height <= 768) {
    return width > 1024 ? 1024 : width;
  }
  if (height <= 960) {
    return width > 1280 ? 1280 : width;
  }
  if (height <= 1050) {
    return width > 1400 ? 1400 : width;
  }
  if (height <= 1080) {
    return width > 1440 ? 1440 : width;
  }
  if (height <= 1200) {
    return width > 1600 ? 1600 : width;
  }
  if (height <= 1392) {
    return width > 1856 ? 1856 : width;
  }
  if (height <= 1440) {
    return width > 1920 ? 1920 : width;
  }
  if (height <= 1536) {
    return width > 2048 ? 2048 : width;
  }
  return width;
}

const VideoPlayer = ({
  index, title, description, link, onProgress, latestProgress,
}) => {
  const { delayedWidth } = useContext(BrowserContext);
  const [time, setTime] = useState(latestProgress < 1 ? latestProgress : 0);
  const playerRef = useRef(null);
  const intervalRef = useRef(null);

  useEffect(() => {
    console.tag(TAG).debug('changing delayed width', delayedWidth);

    const asyncEffect = async () => {
      const player = playerRef.current;
      if (player) {
        const playerWidth = await player.getVideoWidth();
        const isFullscreen = document.fullscreenElement
        || document.webkitFullscreenElement
        || document.mozFullScreenElement
        || document.msFullscreenElement
        || playerWidth === delayedWidth;
        console.tag(TAG).debug('fullscreen detected', isFullscreen);

        const isMobile = window.innerWidth <= STYLES.breakpoints.phoneWidth;
        const isLandscape = window.innerWidth > window.innerHeight;

        if (isFullscreen || (isLandscape && isMobile)) {
          console.tag(TAG).debug('isLandscape, isMobile', isLandscape, isMobile);
          return;
        }

        removeEventListeners(playerRef);
        playerRef.current = null;
        player.pause();
        player.destroy();
      }

      if (intervalRef.current) {
        removeInterval(intervalRef);
        intervalRef.current = null;
      }

      const options = { id: link, width: computeRatio(delayedWidth) };
      const newPlayer = new Player(`frame-${title}-${index}`, options);
      playerRef.current = newPlayer;

      if (time) {
        newPlayer.getDuration().then((duration) => {
          const seconds = duration * time;
          newPlayer.setCurrentTime(seconds);
        });
      }

      const keepTrackProgress = async () => {
        // gets duration of video in seconds
        const duration = await newPlayer.getDuration();

        intervalRef.current = window.setInterval(() => {
          newPlayer.getCurrentTime().then((seconds) => {
            // `seconds` indicates the current playback position of the video
            const progress = seconds / duration;
            console.tag(TAG).debug(`progress: ${progress}, duration ${duration}, seconds ${seconds}`);
            onProgress(progress);
            setTime(progress);
          });
          // track every next 10 seconds of progress
        }, 10000);
      };

      newPlayer.on('ended', () => {
        removeInterval(intervalRef);
        intervalRef.current = null;
        onProgress(1);
        setTime(1);
      });

      newPlayer.on('pause', ({ duration, seconds }) => {
        removeInterval(intervalRef);
        intervalRef.current = null;
        const progress = seconds / duration;
        console.tag(TAG).debug(`progress at paused: ${progress}, duration ${duration}, seconds ${seconds}`);
        onProgress(progress);
        setTime(progress);
      });

      newPlayer.on('play', () => {
        keepTrackProgress();
      });
    };

    asyncEffect();
    return () => {
      removeInterval(intervalRef);
      removeEventListeners(playerRef);
    };
  }, [delayedWidth]);

  return (
    <div className="video-player">
      <div id={`frame-${title}-${index}`} className="frame-wrapper" />
      <div className="details">
        <h1>{title}</h1>
        <p>{description}</p>
      </div>
    </div>
  );
};


VideoPlayer.propTypes = {
  index: PropTypes.number.isRequired,
  title: PropTypes.string.isRequired,
  description: PropTypes.string.isRequired,
  link: PropTypes.string.isRequired,
  onProgress: PropTypes.func.isRequired,
  latestProgress: PropTypes.number.isRequired,
};

export default VideoPlayer;

我正在使用 u-wave vimeo 播放器,其中的响应已得到处理。

import Vimeo from '@u-wave/react-vimeo';`

<Vimeo
    video={videoURL}
    autoplay={false}
    controls={true}
    responsive={true}
    onEnd={() => { onVideoEnd() }}
    style={{ justifyContent: 'center'}}
    start={0}
    onTimeUpdate={(duration) => { console.log(duration }} />

@vimeo/player 包背后的团队发布了新的全屏方法!它非常有效,解决了我的问题。

如果您 运行 遇到类似问题,请查看更新后的@vimeo/player 文档:https://www.npmjs.com/package/@vimeo/player#getfullscreen-promiseboolean-error

我更新和测试的代码如下所示:

import React, {
  useEffect, useContext, useState, useRef,
} from 'react';
import PropTypes from 'prop-types';
import Player from '@vimeo/player';

import { BrowserContext } from '../../contexts/BrowserContext';

const TAG = 'player';

/**
 * remove event listeners
 * @param {object} player
 */
function removeEventListeners(player) {
  if (!player) return;
  player.off('ended');
  player.off('pause');
  player.off('play');
}
/**
 * remove interval
 * @param {number} interval
 */
function removeInterval(interval) {
  console.tag(TAG).debug('removeInterval called');
  window.clearInterval(interval);
}

/**
 * 640×480, 800×600, 960×720, 1024×768, 1280×960,
 * 1400×1050, 1440×1080 , 1600×1200, 1856×1392, 1920×1440, and 2048×1536
 * @param {number} width
 */
function computeRatio(delayedWidth, percentage = 0.9) {
  const height = window.innerHeight;
  const width = delayedWidth - (delayedWidth * (1 - percentage));

  if (height <= 480) {
    return width > 640 ? 640 : width;
  }
  if (height <= 600) {
    return width > 800 ? 800 : width;
  }
  if (height <= 720) {
    return width > 960 ? 960 : width;
  }
  if (height <= 768) {
    return width > 1024 ? 1024 : width;
  }
  if (height <= 960) {
    return width > 1280 ? 1280 : width;
  }
  if (height <= 1050) {
    return width > 1400 ? 1400 : width;
  }
  if (height <= 1080) {
    return width > 1440 ? 1440 : width;
  }
  if (height <= 1200) {
    return width > 1600 ? 1600 : width;
  }
  if (height <= 1392) {
    return width > 1856 ? 1856 : width;
  }
  if (height <= 1440) {
    return width > 1920 ? 1920 : width;
  }
  if (height <= 1536) {
    return width > 2048 ? 2048 : width;
  }
  return width;
}

const VideoPlayer = ({
  index, link, onProgress, latestProgress, widthPercentage, onVideoEnded,
}) => {
  const { delayedWidth } = useContext(BrowserContext);
  const [progress, setProgress] = useState(latestProgress < 1 ? latestProgress : 0);
  const playerRef = useRef(null);
  const intervalRef = useRef(null);

  useEffect(() => {
    console.tag(TAG).debug('changing delayed width', delayedWidth);

    const asyncEffect = async () => {
      const player = playerRef.current;
      if (player) {
        console.tag(TAG).debug('player detected, checking fullscreen');
        const isFullscreen = await player.getFullscreen();
        console.tag(TAG).debug('fullscreen detected', isFullscreen);

        if (isFullscreen) {
          return;
        }

        removeEventListeners(player);
        playerRef.current = null;
        player.pause(); // gets rid of interval
        player.destroy();
      }

      const options = { id: link, width: computeRatio(delayedWidth, widthPercentage) };
      const newPlayer = new Player(`frame-${index}`, options);
      playerRef.current = newPlayer;

      if (progress) {
        newPlayer.getDuration().then((duration) => {
          const seconds = duration * progress;
          newPlayer.setCurrentTime(seconds);
        });
      }

      const keepTrackProgress = async () => {
        // gets duration of video in seconds
        const duration = await newPlayer.getDuration();

        intervalRef.current = window.setInterval(() => {
          const currentPlayer = playerRef.current;
          if (!currentPlayer) {
            return;
          }
          currentPlayer.getCurrentTime().then((seconds) => {
            // `seconds` indicates the current playback position of the video
            const newProgress = seconds / duration;
            console.tag(TAG).debug(`progress: ${newProgress}, duration ${duration}, seconds ${seconds}`);
            onProgress(newProgress);
            setProgress(newProgress);
          });
          // track every next 10 seconds of progress
        }, 10000);
      };

      newPlayer.on('ended', () => {
        console.tag(TAG).debug('player onEnded');
        removeInterval(intervalRef.current);
        intervalRef.current = null;
        onProgress(1);
        setProgress(1);
        onVideoEnded();
      });

      newPlayer.on('pause', ({ duration, seconds }) => {
        console.tag(TAG).debug('player onPause');
        removeInterval(intervalRef.current);
        intervalRef.current = null;
        const newProgress = seconds / duration;
        console.tag(TAG).debug(`progress at paused: ${newProgress}, duration ${duration}, seconds ${seconds}`);
        onProgress(newProgress);
        setProgress(newProgress);
      });

      newPlayer.on('play', () => {
        console.tag(TAG).debug('player onPlay');
        keepTrackProgress();
      });
    };

    asyncEffect();
  }, [delayedWidth]);

  useEffect(() => () => {
    removeInterval(intervalRef.current);
    removeEventListeners(playerRef.current);
    if (playerRef.current) {
      playerRef.current.destroy();
    }
  }, []);

  return (
    <div id={`frame-${index}`} className="frame-wrapper" />
  );
};


VideoPlayer.propTypes = {
  index: PropTypes.number.isRequired,
  link: PropTypes.string.isRequired,
  onProgress: PropTypes.func.isRequired,
  onVideoEnded: PropTypes.func,
  latestProgress: PropTypes.number.isRequired,
  widthPercentage: PropTypes.number,
};

VideoPlayer.defaultProps = {
  widthPercentage: 0.9,
  onVideoEnded: () => {},
};

export default VideoPlayer;