aboutsummaryrefslogtreecommitdiffstats
path: root/src/js
diff options
context:
space:
mode:
authorSam Potts <sam@potts.es>2019-01-14 00:33:48 +1100
committerSam Potts <sam@potts.es>2019-01-14 00:33:48 +1100
commit6782737009bec028b393dbfb8c9897cd0c6df48f (patch)
treeb60c7a6bce9d78e9b8b3244c254fc4e234b0165c /src/js
parent6be125db8762c7ee8d29282ff5bcd08e6bcee042 (diff)
downloadplyr-6782737009bec028b393dbfb8c9897cd0c6df48f.tar.lz
plyr-6782737009bec028b393dbfb8c9897cd0c6df48f.tar.xz
plyr-6782737009bec028b393dbfb8c9897cd0c6df48f.zip
Fullscreen fixes
Diffstat (limited to 'src/js')
-rw-r--r--src/js/config/defaults.js2
-rw-r--r--src/js/controls.js29
-rw-r--r--src/js/fullscreen.js26
-rw-r--r--src/js/listeners.js79
-rw-r--r--src/js/plugins/vimeo.js49
-rw-r--r--src/js/plugins/youtube.js9
-rw-r--r--src/js/plyr.js2
-rw-r--r--src/js/utils/browser.js1
-rw-r--r--src/js/utils/style.js40
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 };