~heckyel/plyr

5fd4391cd923bac874f67413cbacc42c77648759 — Sam Potts 2 years ago b050fde
fix: fullscreen issues with Vimeo (fixes #2175)
4 files changed, 67 insertions(+), 72 deletions(-)

M src/js/listeners.js
M src/js/utils/style.js
M src/sass/lib/mixins.scss
M src/sass/types/video.scss
M src/js/listeners.js => src/js/listeners.js +41 -45
@@ 10,7 10,7 @@ import { getElement, getElements, matches, toggleClass } from './utils/elements'
import { off, on, once, toggleListener, triggerEvent } from './utils/events';
import is from './utils/is';
import { silencePromise } from './utils/promise';
import { getAspectRatio, setAspectRatio } from './utils/style';
import { getAspectRatio, getViewportSize, supportsCSS } from './utils/style';

class Listeners {
  constructor(player) {


@@ 149,16 149,16 @@ class Listeners {
          break;

        /* case 73:
                    this.setLoop('start');
                    break;
          this.setLoop('start');
          break;

                case 76:
                    this.setLoop();
                    break;
        case 76:
          this.setLoop();
          break;

                case 79:
                    this.setLoop('end');
                    break; */
        case 79:
          this.setLoop('end');
          break; */

        default:
          break;


@@ 305,39 305,49 @@ class Listeners {
    );

    // Set a gutter for Vimeo
    const setGutter = (ratio, padding, toggle) => {
    const setGutter = () => {
      if (!player.isVimeo || player.config.vimeo.premium) {
        return;
      }

      const target = player.elements.wrapper.firstChild;
      const [, y] = ratio;
      const [videoX, videoY] = getAspectRatio.call(player);

      target.style.maxWidth = toggle ? `${(y / videoY) * videoX}px` : null;
      target.style.margin = toggle ? '0 auto' : null;
    };
      const target = elements.wrapper;
      const { active } = player.fullscreen;
      const [videoWidth, videoHeight] = getAspectRatio.call(player);
      const useNativeAspectRatio = supportsCSS(`aspect-ratio: ${videoWidth} / ${videoHeight}`);

    // Resize on fullscreen change
    const setPlayerSize = (measure) => {
      // If we don't need to measure the viewport
      if (!measure) {
        return setAspectRatio.call(player);
      // If not active, remove styles
      if (!active) {
        if (useNativeAspectRatio) {
          target.style.width = null;
          target.style.height = null;
        } else {
          target.style.maxWidth = null;
          target.style.margin = null;
        }
        return;
      }

      const rect = elements.container.getBoundingClientRect();
      const { width, height } = rect;
      // Determine which dimension will overflow and constrain view
      const [viewportWidth, viewportHeight] = getViewportSize();
      const overflow = viewportWidth / viewportHeight > videoWidth / videoHeight;

      return setAspectRatio.call(player, `${width}:${height}`);
      if (useNativeAspectRatio) {
        target.style.width = overflow ? 'auto' : '100%';
        target.style.height = overflow ? '100%' : 'auto';
      } else {
        target.style.maxWidth = overflow ? `${(viewportHeight / videoHeight) * videoWidth}px` : null;
        target.style.margin = overflow ? '0 auto' : null;
      }
    };

    // Handle resizing
    const resized = () => {
      clearTimeout(timers.resized);
      timers.resized = setTimeout(setPlayerSize, 50);
      timers.resized = setTimeout(setGutter, 50);
    };

    on.call(player, elements.container, 'enterfullscreen exitfullscreen', (event) => {
      const { target, usingNative } = player.fullscreen;
      const { target } = player.fullscreen;

      // Ignore events not from target
      if (target !== elements.container) {


@@ 349,26 359,12 @@ class Listeners {
        return;
      }

      const isEnter = event.type === 'enterfullscreen';
      // Set the player size when entering fullscreen to viewport size
      const { padding, ratio } = setPlayerSize(isEnter);

      // Set Vimeo gutter
      setGutter(ratio, padding, isEnter);

      // Horrible hack for Safari 14 not repainting properly on entering fullscreen
      if (isEnter) {
        setTimeout(() => repaint(elements.container), 100);
      }
      setGutter();

      // If not using native browser fullscreen API, we need to check for resizes of viewport
      if (!usingNative) {
        if (isEnter) {
          on.call(player, window, 'resize', resized);
        } else {
          off.call(player, window, 'resize', resized);
        }
      }
      // Watch for resizes
      const method = event.type === 'enterfullscreen' ? on : off;
      method.call(player, window, 'resize', resized);
    });
  };


M src/js/utils/style.js => src/js/utils/style.js +21 -6
@@ 5,6 5,15 @@
import { closest } from './arrays';
import is from './is';

// Check support for a CSS declaration
export function supportsCSS(declaration) {
  if (!window || !window.CSS) {
    return false;
  }

  return window.CSS.supports(declaration);
}

// Standard/common aspect ratios
const standardRatios = [
  [1, 1],


@@ 67,10 76,10 @@ export function getAspectRatio(input) {
  // Get from HTML5 video
  if (ratio === null && this.isHTML5) {
    const { videoWidth, videoHeight } = this.media;
    ratio = reduceAspectRatio([videoWidth, videoHeight]);
    ratio = [videoWidth, videoHeight];
  }

  return ratio;
  return reduceAspectRatio(ratio);
}

// Set aspect ratio for responsive container


@@ 86,8 95,8 @@ export function setAspectRatio(input) {
    return {};
  }

  const [x, y] = ratio;
  const useNative = window.CSS ? window.CSS.supports(`aspect-ratio: ${x}/${y}`) : false;
  const [x, y] = reduceAspectRatio(ratio);
  const useNative = supportsCSS(`aspect-ratio: ${x}/${y}`);
  const padding = (100 / x) * y;

  if (useNative) {


@@ 107,7 116,7 @@ export function setAspectRatio(input) {
      this.media.style.transform = `translateY(-${offset}%)`;
    }
  } else if (this.isHTML5) {
    wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
    wrapper.classList.add(this.config.classNames.videoFixedRatio);
  }

  return { padding, ratio };


@@ 127,4 136,10 @@ export function roundAspectRatio(x, y, tolerance = 0.05) {
  return [x, y];
}

export default { setAspectRatio };
// Get the size of the viewport
// https://stackoverflow.com/questions/1248081/how-to-get-the-browser-viewport-dimensions
export function getViewportSize() {
  const width = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
  const height = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
  return [width, height];
}

M src/sass/lib/mixins.scss => src/sass/lib/mixins.scss +0 -11
@@ 59,17 59,6 @@
    height: 100%;
  }

  .plyr__video-wrapper {
    height: 100%;
    position: static;
  }

  // Vimeo requires some different styling
  &.plyr--vimeo .plyr__video-wrapper {
    height: 0;
    position: relative;
  }

  // Display correct icon
  .plyr__control .icon--exit-fullscreen {
    display: block;

M src/sass/types/video.scss => src/sass/types/video.scss +5 -10
@@ 14,7 14,6 @@

.plyr__video-wrapper {
  background: var(--plyr-video-background, $plyr-video-background);
  height: 100%;
  margin: auto;
  overflow: hidden;
  position: relative;


@@ 45,17 44,13 @@ $embed-padding: ((100 / 16) * 9);
  width: 100%;
}

// If the full custom UI is supported
.plyr--full-ui .plyr__video-embed {
// For Vimeo, if the full custom UI is supported
.plyr--full-ui .plyr__video-embed > .plyr__video-embed__container {
  $height: 240;
  $offset: to-percentage(($height - $embed-padding) / ($height / 50));

  // Only used for Vimeo
  > .plyr__video-embed__container {
    padding-bottom: to-percentage($height);
    position: relative;
    transform: translateY(-$offset);
  }
  padding-bottom: to-percentage($height);
  position: relative;
  transform: translateY(-$offset);
}

// Controls container