aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/js/config/defaults.js2
-rw-r--r--src/js/listeners.js86
-rw-r--r--src/js/media.js1
-rw-r--r--src/js/plugins/vimeo.js9
-rw-r--r--src/js/plugins/youtube.js4
-rw-r--r--src/js/plyr.js8
-rw-r--r--src/js/plyr.polyfilled.js2
-rw-r--r--src/js/utils/browser.js6
-rw-r--r--src/js/utils/style.js80
-rw-r--r--src/sass/components/poster.scss5
-rw-r--r--src/sass/lib/mixins.scss11
-rw-r--r--src/sass/types/video.scss26
12 files changed, 149 insertions, 91 deletions
diff --git a/src/js/config/defaults.js b/src/js/config/defaults.js
index 7a73c318..77117588 100644
--- a/src/js/config/defaults.js
+++ b/src/js/config/defaults.js
@@ -61,7 +61,7 @@ const defaults = {
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
- iconUrl: 'https://cdn.plyr.io/3.6.4/plyr.svg',
+ iconUrl: 'https://cdn.plyr.io/3.6.7/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
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);
-
- 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);
});
};
diff --git a/src/js/media.js b/src/js/media.js
index ddac5ebf..4584fea3 100644
--- a/src/js/media.js
+++ b/src/js/media.js
@@ -41,7 +41,6 @@ const media = {
// Poster image container
this.elements.poster = createElement('div', {
class: this.config.classNames.poster,
- hidden: '',
});
this.elements.wrapper.appendChild(this.elements.poster);
diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js
index 5fe41aba..10246c66 100644
--- a/src/js/plugins/vimeo.js
+++ b/src/js/plugins/vimeo.js
@@ -11,7 +11,7 @@ import fetch from '../utils/fetch';
import is from '../utils/is';
import loadScript from '../utils/load-script';
import { format, stripHTML } from '../utils/strings';
-import { setAspectRatio } from '../utils/style';
+import { roundAspectRatio, setAspectRatio } from '../utils/style';
import { buildUrlParams } from '../utils/urls';
// Parse Vimeo ID from URL
@@ -104,7 +104,10 @@ const vimeo = {
const src = format(player.config.urls.vimeo.iframe, id, params);
iframe.setAttribute('src', src);
iframe.setAttribute('allowfullscreen', '');
- iframe.setAttribute('allow', ['autoplay', 'fullscreen', 'picture-in-picture'].join('; '));
+ iframe.setAttribute(
+ 'allow',
+ ['autoplay', 'fullscreen', 'picture-in-picture', 'encrypted-media', 'accelerometer', 'gyroscope'].join('; '),
+ );
// Set the referrer policy if required
if (!is.empty(referrerPolicy)) {
@@ -291,7 +294,7 @@ const vimeo = {
// Set aspect ratio based on video size
Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then((dimensions) => {
const [width, height] = dimensions;
- player.embed.ratio = [width, height];
+ player.embed.ratio = roundAspectRatio(width, height);
setAspectRatio.call(this);
});
diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js
index db5781e6..19d2f1a5 100644
--- a/src/js/plugins/youtube.js
+++ b/src/js/plugins/youtube.js
@@ -11,7 +11,7 @@ import loadImage from '../utils/load-image';
import loadScript from '../utils/load-script';
import { extend } from '../utils/objects';
import { format, generateId } from '../utils/strings';
-import { setAspectRatio } from '../utils/style';
+import { roundAspectRatio, setAspectRatio } from '../utils/style';
// Parse YouTube ID from URL
function parseId(url) {
@@ -90,7 +90,7 @@ const youtube = {
ui.setTitle.call(this);
// Set aspect ratio
- this.embed.ratio = [width, height];
+ this.embed.ratio = roundAspectRatio(width, height);
}
setAspectRatio.call(this);
diff --git a/src/js/plyr.js b/src/js/plyr.js
index b40f5c5a..a7876c2a 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -1,6 +1,6 @@
// ==========================================================================
// Plyr
-// plyr.js v3.6.4
+// plyr.js v3.6.7
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
@@ -29,7 +29,7 @@ import loadSprite from './utils/load-sprite';
import { clamp } from './utils/numbers';
import { cloneDeep, extend } from './utils/objects';
import { silencePromise } from './utils/promise';
-import { getAspectRatio, reduceAspectRatio, setAspectRatio, validateRatio } from './utils/style';
+import { getAspectRatio, reduceAspectRatio, setAspectRatio, validateAspectRatio } from './utils/style';
import { parseUrl } from './utils/urls';
// Private properties
@@ -916,12 +916,12 @@ class Plyr {
return;
}
- if (!is.string(input) || !validateRatio(input)) {
+ if (!is.string(input) || !validateAspectRatio(input)) {
this.debug.error(`Invalid aspect ratio specified (${input})`);
return;
}
- this.config.ratio = input;
+ this.config.ratio = reduceAspectRatio(input);
setAspectRatio.call(this);
}
diff --git a/src/js/plyr.polyfilled.js b/src/js/plyr.polyfilled.js
index f8f613e7..d40d5d97 100644
--- a/src/js/plyr.polyfilled.js
+++ b/src/js/plyr.polyfilled.js
@@ -1,6 +1,6 @@
// ==========================================================================
// Plyr Polyfilled Build
-// plyr.js v3.6.4
+// plyr.js v3.6.7
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
diff --git a/src/js/utils/browser.js b/src/js/utils/browser.js
index 7b8fa5e2..6b5e768a 100644
--- a/src/js/utils/browser.js
+++ b/src/js/utils/browser.js
@@ -4,11 +4,13 @@
// ==========================================================================
const browser = {
- isIE: /* @cc_on!@ */ false || !!document.documentMode,
+ isIE: Boolean(window.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),
+ isIos:
+ (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) ||
+ /(iPad|iPhone|iPod)/gi.test(navigator.platform),
};
export default browser;
diff --git a/src/js/utils/style.js b/src/js/utils/style.js
index f02b0ba5..e05e058f 100644
--- a/src/js/utils/style.js
+++ b/src/js/utils/style.js
@@ -2,9 +2,39 @@
// Style utils
// ==========================================================================
+import { closest } from './arrays';
import is from './is';
-export function validateRatio(input) {
+// 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],
+ [4, 3],
+ [3, 4],
+ [5, 4],
+ [4, 5],
+ [3, 2],
+ [2, 3],
+ [16, 10],
+ [10, 16],
+ [16, 9],
+ [9, 16],
+ [21, 9],
+ [9, 21],
+ [32, 9],
+ [9, 32],
+].reduce((out, [x, y]) => ({ ...out, [x / y]: [x, y] }), {});
+
+// Validate an aspect ratio
+export function validateAspectRatio(input) {
if (!is.array(input) && (!is.string(input) || !input.includes(':'))) {
return false;
}
@@ -14,6 +44,7 @@ export function validateRatio(input) {
return ratio.map(Number).every(is.number);
}
+// Reduce an aspect ratio to it's lowest form
export function reduceAspectRatio(ratio) {
if (!is.array(ratio) || !ratio.every(is.number)) {
return null;
@@ -26,8 +57,9 @@ export function reduceAspectRatio(ratio) {
return [width / divider, height / divider];
}
+// Calculate an aspect ratio
export function getAspectRatio(input) {
- const parse = (ratio) => (validateRatio(ratio) ? ratio.split(':').map(Number) : null);
+ const parse = (ratio) => (validateAspectRatio(ratio) ? ratio.split(':').map(Number) : null);
// Try provided ratio
let ratio = parse(input);
@@ -44,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
@@ -58,10 +90,20 @@ export function setAspectRatio(input) {
const { wrapper } = this.elements;
const ratio = getAspectRatio.call(this, input);
- const [w, h] = is.array(ratio) ? ratio : [0, 0];
- const padding = (100 / w) * h;
- wrapper.style.paddingBottom = `${padding}%`;
+ if (!is.array(ratio)) {
+ return {};
+ }
+
+ const [x, y] = reduceAspectRatio(ratio);
+ const useNative = supportsCSS(`aspect-ratio: ${x}/${y}`);
+ const padding = (100 / x) * y;
+
+ if (useNative) {
+ wrapper.style.aspectRatio = `${x}/${y}`;
+ } else {
+ wrapper.style.paddingBottom = `${padding}%`;
+ }
// For Vimeo we have an extra <div> to hide the standard controls and UI
if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) {
@@ -74,10 +116,30 @@ 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 };
}
-export default { setAspectRatio };
+// Round an aspect ratio to closest standard ratio
+export function roundAspectRatio(x, y, tolerance = 0.05) {
+ const ratio = x / y;
+ const closestRatio = closest(Object.keys(standardRatios), ratio);
+
+ // Check match is within tolerance
+ if (Math.abs(closestRatio - ratio) <= tolerance) {
+ return standardRatios[closestRatio];
+ }
+
+ // No match
+ return [x, y];
+}
+
+// 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/components/poster.scss b/src/sass/components/poster.scss
index 3a158c1b..788d819c 100644
--- a/src/sass/components/poster.scss
+++ b/src/sass/components/poster.scss
@@ -20,3 +20,8 @@
.plyr--stopped.plyr__poster-enabled .plyr__poster {
opacity: 1;
}
+
+// Allow interaction with YouTube controls while paused
+.plyr--youtube.plyr--paused.plyr__poster-enabled:not(.plyr--stopped) .plyr__poster {
+ display: none;
+}
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 9a10d5ea..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;
@@ -26,29 +25,32 @@ $embed-padding: ((100 / 16) * 9);
.plyr__video-embed,
.plyr__video-wrapper--fixed-ratio {
- height: 0;
- padding-bottom: to-percentage($embed-padding);
+ @supports not (aspect-ratio: 16 / 9) {
+ height: 0;
+ padding-bottom: to-percentage($embed-padding);
+ position: relative;
+ }
+
+ aspect-ratio: 16 / 9;
}
.plyr__video-embed iframe,
.plyr__video-wrapper--fixed-ratio video {
border: 0;
+ height: 100%;
left: 0;
position: absolute;
top: 0;
+ 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