From 5fd4391cd923bac874f67413cbacc42c77648759 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Wed, 12 May 2021 23:31:01 +1000 Subject: [PATCH] fix: fullscreen issues with Vimeo (fixes #2175) --- src/js/listeners.js | 86 +++++++++++++++++++-------------------- src/js/utils/style.js | 27 +++++++++--- src/sass/lib/mixins.scss | 11 ----- src/sass/types/video.scss | 15 +++---- 4 files changed, 67 insertions(+), 72 deletions(-) diff --git a/src/js/listeners.js b/src/js/listeners.js index 3d1f8ef0..c490070c 100644 --- a/src/js/listeners.js +++ b/src/js/listeners.js @@ -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); + const target = elements.wrapper; + const { active } = player.fullscreen; + const [videoWidth, videoHeight] = getAspectRatio.call(player); + const useNativeAspectRatio = supportsCSS(`aspect-ratio: ${videoWidth} / ${videoHeight}`); - target.style.maxWidth = toggle ? `${(y / videoY) * videoX}px` : null; - target.style.margin = toggle ? '0 auto' : null; - }; - - // 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); + setGutter(); - // Horrible hack for Safari 14 not repainting properly on entering fullscreen - if (isEnter) { - setTimeout(() => repaint(elements.container), 100); - } - - // 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); }); }; diff --git a/src/js/utils/style.js b/src/js/utils/style.js index 1032ed2a..e05e058f 100644 --- a/src/js/utils/style.js +++ b/src/js/utils/style.js @@ -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]; +} diff --git a/src/sass/lib/mixins.scss b/src/sass/lib/mixins.scss index cbb8cc78..d1bc4b3e 100644 --- a/src/sass/lib/mixins.scss +++ b/src/sass/lib/mixins.scss @@ -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; diff --git a/src/sass/types/video.scss b/src/sass/types/video.scss index fe7d8b37..e0de7acc 100644 --- a/src/sass/types/video.scss +++ b/src/sass/types/video.scss @@ -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