diff options
author | Sam Potts <sam@potts.es> | 2019-01-14 00:33:48 +1100 |
---|---|---|
committer | Sam Potts <sam@potts.es> | 2019-01-14 00:33:48 +1100 |
commit | 6782737009bec028b393dbfb8c9897cd0c6df48f (patch) | |
tree | b60c7a6bce9d78e9b8b3244c254fc4e234b0165c /src/js | |
parent | 6be125db8762c7ee8d29282ff5bcd08e6bcee042 (diff) | |
download | plyr-6782737009bec028b393dbfb8c9897cd0c6df48f.tar.lz plyr-6782737009bec028b393dbfb8c9897cd0c6df48f.tar.xz plyr-6782737009bec028b393dbfb8c9897cd0c6df48f.zip |
Fullscreen fixes
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/config/defaults.js | 2 | ||||
-rw-r--r-- | src/js/controls.js | 29 | ||||
-rw-r--r-- | src/js/fullscreen.js | 26 | ||||
-rw-r--r-- | src/js/listeners.js | 79 | ||||
-rw-r--r-- | src/js/plugins/vimeo.js | 49 | ||||
-rw-r--r-- | src/js/plugins/youtube.js | 9 | ||||
-rw-r--r-- | src/js/plyr.js | 2 | ||||
-rw-r--r-- | src/js/utils/browser.js | 1 | ||||
-rw-r--r-- | src/js/utils/style.js | 40 |
9 files changed, 168 insertions, 69 deletions
diff --git a/src/js/config/defaults.js b/src/js/config/defaults.js index 5b86cceb..ae873eda 100644 --- a/src/js/config/defaults.js +++ b/src/js/config/defaults.js @@ -108,7 +108,7 @@ const defaults = { // Fullscreen settings fullscreen: { enabled: true, // Allow fullscreen? - fallback: true, // Fallback for vintage browsers + fallback: true, // Fallback using full viewport/window iosNative: false, // Use the native fullscreen in iOS (disables custom controls) }, diff --git a/src/js/controls.js b/src/js/controls.js index f414f6d6..953ca293 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -9,7 +9,20 @@ import support from './support'; import { repaint, transitionEndEvent } from './utils/animation'; import { dedupe } from './utils/arrays'; import browser from './utils/browser'; -import { createElement, emptyElement, getAttributesFromSelector, getElement, getElements, hasClass, matches, removeElement, setAttributes, setFocus, toggleClass, toggleHidden } from './utils/elements'; +import { + createElement, + emptyElement, + getAttributesFromSelector, + getElement, + getElements, + hasClass, + matches, + removeElement, + setAttributes, + setFocus, + toggleClass, + toggleHidden, +} from './utils/elements'; import { off, on } from './utils/events'; import i18n from './utils/i18n'; import is from './utils/is'; @@ -667,7 +680,7 @@ const controls = { } // Set CSS custom property - range.style.setProperty('--value', `${range.value / range.max * 100}%`); + range.style.setProperty('--value', `${(range.value / range.max) * 100}%`); }, // Update hover tooltip for seeking @@ -699,7 +712,7 @@ const controls = { // Determine percentage, if already visible if (is.event(event)) { - percent = 100 / clientRect.width * (event.pageX - clientRect.left); + percent = (100 / clientRect.width) * (event.pageX - clientRect.left); } else if (hasClass(this.elements.display.seekTooltip, visible)) { percent = parseFloat(this.elements.display.seekTooltip.style.left, 10); } else { @@ -714,7 +727,7 @@ const controls = { } // Display the time a click would seek to - controls.updateTimeDisplay.call(this, this.elements.display.seekTooltip, this.duration / 100 * percent); + controls.updateTimeDisplay.call(this, this.elements.display.seekTooltip, (this.duration / 100) * percent); // Set position this.elements.display.seekTooltip.style.left = `${percent}%`; @@ -1674,15 +1687,17 @@ const controls = { .filter(Boolean) .forEach(button => { if (is.array(button) || is.nodeList(button)) { - Array.from(button).filter(Boolean).forEach(addProperty); + Array.from(button) + .filter(Boolean) + .forEach(addProperty); } else { addProperty(button); } }); } - // Edge sometimes doesn't finish the paint so force a redraw - if (window.navigator.userAgent.includes('Edge')) { + // Edge sometimes doesn't finish the paint so force a repaint + if (browser.isEdge) { repaint(target); } diff --git a/src/js/fullscreen.js b/src/js/fullscreen.js index 9c21b82a..c86bf877 100644 --- a/src/js/fullscreen.js +++ b/src/js/fullscreen.js @@ -94,6 +94,9 @@ class Fullscreen { // Scroll position this.scrollPosition = { x: 0, y: 0 }; + // Force the use of 'full window/browser' rather than fullscreen + this.forceFallback = player.config.fullscreen.fallback === 'force'; + // Register event listeners // Handle event (incase user presses escape etc) on.call( @@ -130,6 +133,11 @@ class Fullscreen { ); } + // If we're actually using native + get usingNative() { + return Fullscreen.native && !this.forceFallback; + } + // Get the prefix for handlers static get prefix() { // No prefix @@ -174,7 +182,7 @@ class Fullscreen { } // Fallback using classname - if (!Fullscreen.native) { + if (!Fullscreen.native || this.forceFallback) { return hasClass(this.target, this.player.config.classNames.fullscreen.fallback); } @@ -193,7 +201,17 @@ class Fullscreen { // Update UI update() { if (this.enabled) { - this.player.debug.log(`${Fullscreen.native ? 'Native' : 'Fallback'} fullscreen enabled`); + let mode; + + if (this.forceFallback) { + mode = 'Fallback (forced)'; + } else if (Fullscreen.native) { + mode = 'Native'; + } else { + mode = 'Fallback'; + } + + this.player.debug.log(`${mode} fullscreen enabled`); } else { this.player.debug.log('Fullscreen not supported and fallback disabled'); } @@ -211,7 +229,7 @@ class Fullscreen { // iOS native fullscreen doesn't need the request step if (browser.isIos && this.player.config.fullscreen.iosNative) { this.target.webkitEnterFullscreen(); - } else if (!Fullscreen.native) { + } else if (!Fullscreen.native || this.forceFallback) { toggleFallback.call(this, true); } else if (!this.prefix) { this.target.requestFullscreen(); @@ -230,7 +248,7 @@ class Fullscreen { if (browser.isIos && this.player.config.fullscreen.iosNative) { this.target.webkitExitFullscreen(); this.player.play(); - } else if (!Fullscreen.native) { + } else if (!Fullscreen.native || this.forceFallback) { toggleFallback.call(this, false); } else if (!this.prefix) { (document.cancelFullScreen || document.exitFullscreen).call(document); diff --git a/src/js/listeners.js b/src/js/listeners.js index f073f5cb..ae9277a5 100644 --- a/src/js/listeners.js +++ b/src/js/listeners.js @@ -7,8 +7,9 @@ import ui from './ui'; import { repaint } from './utils/animation'; import browser from './utils/browser'; import { getElement, getElements, matches, toggleClass, toggleHidden } from './utils/elements'; -import { on, once, toggleListener, triggerEvent } from './utils/events'; +import { off, on, once, toggleListener, triggerEvent } from './utils/events'; import is from './utils/is'; +import { setAspectRatio } from './utils/style'; class Listeners { constructor(player) { @@ -164,7 +165,7 @@ class Listeners { // Escape is handle natively when in full screen // So we only need to worry about non native - if (!player.fullscreen.enabled && player.fullscreen.active && code === 27) { + if (code === 27 && !player.fullscreen.usingNative && player.fullscreen.active) { player.fullscreen.toggle(); } @@ -261,10 +262,10 @@ class Listeners { // Container listeners container() { const { player } = this; - const { elements } = player; + const { config, elements, timers } = player; // Keyboard shortcuts - if (!player.config.keyboard.global && player.config.keyboard.focused) { + if (!config.keyboard.global && config.keyboard.focused) { on.call(player, elements.container, 'keydown keyup', this.handleKey, false); } @@ -294,12 +295,78 @@ class Listeners { } // Clear timer - clearTimeout(player.timers.controls); + clearTimeout(timers.controls); // Set new timer to prevent flicker when seeking - player.timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay); + timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay); }, ); + + // Force edge to repaint on exit fullscreen + // TODO: Fix weird bug where Edge doesn't re-draw when exiting fullscreen + /* if (browser.isEdge) { + on.call(player, elements.container, 'exitfullscreen', () => { + setTimeout(() => repaint(elements.container), 100); + }); + } */ + + // Set a gutter for Vimeo + const setGutter = (ratio, padding, toggle) => { + if (!player.isVimeo) { + return; + } + + const target = player.elements.wrapper.firstChild; + const [, height] = ratio.split(':').map(Number); + const [videoWidth, videoHeight] = player.embed.ratio.split(':').map(Number); + + target.style.maxWidth = toggle ? `${(height / videoHeight) * videoWidth}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); + } + + const rect = elements.container.getBoundingClientRect(); + const { width, height } = rect; + + return setAspectRatio.call(player, `${width}:${height}`); + }; + + const resized = () => { + window.clearTimeout(timers.resized); + timers.resized = window.setTimeout(setPlayerSize, 50); + }; + + on.call(player, elements.container, 'enterfullscreen exitfullscreen', event => { + const { target, usingNative } = player.fullscreen; + + // Ignore for iOS native + if (!player.isEmbed || target !== elements.container) { + 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); + + // If not using native fullscreen, 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); + } + } + }); } // Listen for media events diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js index 2d9ba6e2..c0bcf8af 100644 --- a/src/js/plugins/vimeo.js +++ b/src/js/plugins/vimeo.js @@ -11,6 +11,7 @@ import fetch from '../utils/fetch'; import is from '../utils/is'; import loadScript from '../utils/loadScript'; import { format, stripHTML } from '../utils/strings'; +import { setAspectRatio } from '../utils/style'; import { buildUrlParams } from '../utils/urls'; // Parse Vimeo ID from URL @@ -27,13 +28,6 @@ function parseId(url) { return url.match(regex) ? RegExp.$2 : url; } -// Get aspect ratio for dimensions -function getAspectRatio(width, height) { - const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h)); - const ratio = getRatio(width, height); - return `${width / ratio}:${height / ratio}`; -} - // Set playback state and trigger change (only on actual change) function assurePlaybackState(play) { if (play && !this.embed.hasPlayed) { @@ -51,7 +45,7 @@ const vimeo = { toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set intial ratio - vimeo.setAspectRatio.call(this); + setAspectRatio.call(this); // Load the API if not already if (!is.object(window.Vimeo)) { @@ -67,22 +61,6 @@ const vimeo = { } }, - // Set aspect ratio - // For Vimeo we have an extra 300% height <div> to hide the standard controls and UI - setAspectRatio(input) { - const [x, y] = (is.string(input) ? input : this.config.ratio).split(':').map(Number); - const padding = (100 / x) * y; - vimeo.padding = padding; - this.elements.wrapper.style.paddingBottom = `${padding}%`; - - if (this.supported.ui) { - const height = 240; - const offset = (height - padding) / (height / 50); - - this.media.style.transform = `translateY(-${offset}%)`; - } - }, - // API Ready ready() { const player = this; @@ -91,7 +69,7 @@ const vimeo = { const options = { loop: player.config.loop.active, autoplay: player.autoplay, - // muted: player.muted, + muted: player.muted, byline: false, portrait: false, title: false, @@ -300,8 +278,9 @@ const vimeo = { // Set aspect ratio based on video size Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => { - vimeo.ratio = getAspectRatio(dimensions[0], dimensions[1]); - vimeo.setAspectRatio.call(this, vimeo.ratio); + const [width, height] = dimensions; + player.embed.ratio = `${width}:${height}`; + setAspectRatio.call(this, player.embed.ratio); }); // Set autopause @@ -405,22 +384,6 @@ const vimeo = { triggerEvent.call(player, player.media, 'error'); }); - // Set height/width on fullscreen - player.on('enterfullscreen exitfullscreen', event => { - const { target } = player.fullscreen; - - // Ignore for iOS native - if (target !== player.elements.container) { - return; - } - - const toggle = event.type === 'enterfullscreen'; - const [x, y] = vimeo.ratio.split(':').map(Number); - const dimension = x > y ? 'width' : 'height'; - - target.style[dimension] = toggle ? `${vimeo.padding}%` : null; - }); - // Rebuild UI setTimeout(() => ui.build.call(player), 0); }, diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index 0cc8fd1d..9d47aa53 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -10,6 +10,7 @@ import is from '../utils/is'; import loadImage from '../utils/loadImage'; import loadScript from '../utils/loadScript'; import { format, generateId } from '../utils/strings'; +import { setAspectRatio } from '../utils/style'; // Parse YouTube ID from URL function parseId(url) { @@ -38,7 +39,7 @@ const youtube = { toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set aspect ratio - youtube.setAspectRatio.call(this); + setAspectRatio.call(this); // Setup API if (is.object(window.YT) && is.function(window.YT.Player)) { @@ -98,12 +99,6 @@ const youtube = { } }, - // Set aspect ratio - setAspectRatio() { - const ratio = this.config.ratio.split(':'); - this.elements.wrapper.style.paddingBottom = `${100 / ratio[0] * ratio[1]}%`; - }, - // API ready ready() { const player = this; diff --git a/src/js/plyr.js b/src/js/plyr.js index 872eb927..1059ce05 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -263,7 +263,7 @@ class Plyr { // Wrap media if (!is.element(this.elements.container)) { - this.elements.container = createElement('div'); + this.elements.container = createElement('div', { tabindex: 0 }); wrap(this.media, this.elements.container); } diff --git a/src/js/utils/browser.js b/src/js/utils/browser.js index d574f683..11705074 100644 --- a/src/js/utils/browser.js +++ b/src/js/utils/browser.js @@ -5,6 +5,7 @@ const browser = { isIE: /* @cc_on!@ */ false || !!document.documentMode, + isEdge: window.navigator.userAgent.includes('Edge'), isWebkit: 'WebkitAppearance' in document.documentElement.style && !/Edge/.test(navigator.userAgent), isIPhone: /(iPhone|iPod)/gi.test(navigator.platform), isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform), diff --git a/src/js/utils/style.js b/src/js/utils/style.js new file mode 100644 index 00000000..a8eb393b --- /dev/null +++ b/src/js/utils/style.js @@ -0,0 +1,40 @@ +// ========================================================================== +// Style utils +// ========================================================================== + +import is from './is'; + +/* function reduceAspectRatio(width, height) { + const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h)); + const ratio = getRatio(width, height); + return `${width / ratio}:${height / ratio}`; +} */ + +// Set aspect ratio for responsive container +export function setAspectRatio(input) { + let ratio = input; + + if (!is.string(ratio) && !is.nullOrUndefined(this.embed)) { + ({ ratio } = this.embed); + } + + if (!is.string(ratio)) { + ({ ratio } = this.config); + } + + const [x, y] = ratio.split(':').map(Number); + const padding = (100 / x) * y; + + this.elements.wrapper.style.paddingBottom = `${padding}%`; + + // For Vimeo we have an extra <div> to hide the standard controls and UI + if (this.isVimeo && this.supported.ui) { + const height = 240; + const offset = (height - padding) / (height / 50); + this.media.style.transform = `translateY(-${offset}%)`; + } + + return { padding, ratio }; +} + +export default { setAspectRatio }; |