fix: aspect ratio improvements (#2171)
- Use CSS aspect-ratio (retain fallback for legacy browsers) - Round aspect ratios (fixes YouTube black border issue)
This commit is contained in:
parent
ddb5ad071e
commit
438e425838
@ -2,4 +2,4 @@
|
|||||||
// Layout
|
// Layout
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
$container-max-width: 1260px;
|
$container-max-width: 1240px;
|
||||||
|
@ -11,7 +11,7 @@ import fetch from '../utils/fetch';
|
|||||||
import is from '../utils/is';
|
import is from '../utils/is';
|
||||||
import loadScript from '../utils/load-script';
|
import loadScript from '../utils/load-script';
|
||||||
import { format, stripHTML } from '../utils/strings';
|
import { format, stripHTML } from '../utils/strings';
|
||||||
import { setAspectRatio } from '../utils/style';
|
import { roundAspectRatio, setAspectRatio } from '../utils/style';
|
||||||
import { buildUrlParams } from '../utils/urls';
|
import { buildUrlParams } from '../utils/urls';
|
||||||
|
|
||||||
// Parse Vimeo ID from URL
|
// Parse Vimeo ID from URL
|
||||||
@ -294,7 +294,7 @@ const vimeo = {
|
|||||||
// Set aspect ratio based on video size
|
// Set aspect ratio based on video size
|
||||||
Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then((dimensions) => {
|
Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then((dimensions) => {
|
||||||
const [width, height] = dimensions;
|
const [width, height] = dimensions;
|
||||||
player.embed.ratio = [width, height];
|
player.embed.ratio = roundAspectRatio(width, height);
|
||||||
setAspectRatio.call(this);
|
setAspectRatio.call(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import loadImage from '../utils/load-image';
|
|||||||
import loadScript from '../utils/load-script';
|
import loadScript from '../utils/load-script';
|
||||||
import { extend } from '../utils/objects';
|
import { extend } from '../utils/objects';
|
||||||
import { format, generateId } from '../utils/strings';
|
import { format, generateId } from '../utils/strings';
|
||||||
import { setAspectRatio } from '../utils/style';
|
import { roundAspectRatio, setAspectRatio } from '../utils/style';
|
||||||
|
|
||||||
// Parse YouTube ID from URL
|
// Parse YouTube ID from URL
|
||||||
function parseId(url) {
|
function parseId(url) {
|
||||||
@ -90,7 +90,7 @@ const youtube = {
|
|||||||
ui.setTitle.call(this);
|
ui.setTitle.call(this);
|
||||||
|
|
||||||
// Set aspect ratio
|
// Set aspect ratio
|
||||||
this.embed.ratio = [width, height];
|
this.embed.ratio = roundAspectRatio(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAspectRatio.call(this);
|
setAspectRatio.call(this);
|
||||||
|
@ -29,7 +29,7 @@ import loadSprite from './utils/load-sprite';
|
|||||||
import { clamp } from './utils/numbers';
|
import { clamp } from './utils/numbers';
|
||||||
import { cloneDeep, extend } from './utils/objects';
|
import { cloneDeep, extend } from './utils/objects';
|
||||||
import { silencePromise } from './utils/promise';
|
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';
|
import { parseUrl } from './utils/urls';
|
||||||
|
|
||||||
// Private properties
|
// Private properties
|
||||||
@ -916,12 +916,12 @@ class Plyr {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is.string(input) || !validateRatio(input)) {
|
if (!is.string(input) || !validateAspectRatio(input)) {
|
||||||
this.debug.error(`Invalid aspect ratio specified (${input})`);
|
this.debug.error(`Invalid aspect ratio specified (${input})`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.config.ratio = input;
|
this.config.ratio = reduceAspectRatio(input);
|
||||||
|
|
||||||
setAspectRatio.call(this);
|
setAspectRatio.call(this);
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,30 @@
|
|||||||
// Style utils
|
// Style utils
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
|
import { closest } from './arrays';
|
||||||
import is from './is';
|
import is from './is';
|
||||||
|
|
||||||
export function validateRatio(input) {
|
// 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(':'))) {
|
if (!is.array(input) && (!is.string(input) || !input.includes(':'))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -14,6 +35,7 @@ export function validateRatio(input) {
|
|||||||
return ratio.map(Number).every(is.number);
|
return ratio.map(Number).every(is.number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reduce an aspect ratio to it's lowest form
|
||||||
export function reduceAspectRatio(ratio) {
|
export function reduceAspectRatio(ratio) {
|
||||||
if (!is.array(ratio) || !ratio.every(is.number)) {
|
if (!is.array(ratio) || !ratio.every(is.number)) {
|
||||||
return null;
|
return null;
|
||||||
@ -26,8 +48,9 @@ export function reduceAspectRatio(ratio) {
|
|||||||
return [width / divider, height / divider];
|
return [width / divider, height / divider];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate an aspect ratio
|
||||||
export function getAspectRatio(input) {
|
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
|
// Try provided ratio
|
||||||
let ratio = parse(input);
|
let ratio = parse(input);
|
||||||
|
|
||||||
@ -58,10 +81,20 @@ export function setAspectRatio(input) {
|
|||||||
|
|
||||||
const { wrapper } = this.elements;
|
const { wrapper } = this.elements;
|
||||||
const ratio = getAspectRatio.call(this, input);
|
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] = ratio;
|
||||||
|
const useNative = window.CSS?.supports(`aspect-ratio: ${x} / ${y}`) ?? false;
|
||||||
|
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
|
// For Vimeo we have an extra <div> to hide the standard controls and UI
|
||||||
if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) {
|
if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) {
|
||||||
@ -80,4 +113,18 @@ export function setAspectRatio(input) {
|
|||||||
return { padding, ratio };
|
return { padding, ratio };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
|
||||||
export default { setAspectRatio };
|
export default { setAspectRatio };
|
||||||
|
@ -26,9 +26,13 @@ $embed-padding: ((100 / 16) * 9);
|
|||||||
|
|
||||||
.plyr__video-embed,
|
.plyr__video-embed,
|
||||||
.plyr__video-wrapper--fixed-ratio {
|
.plyr__video-wrapper--fixed-ratio {
|
||||||
height: 0;
|
@supports not (aspect-ratio: 16 / 9) {
|
||||||
padding-bottom: to-percentage($embed-padding);
|
height: 0;
|
||||||
position: relative;
|
padding-bottom: to-percentage($embed-padding);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plyr__video-embed iframe,
|
.plyr__video-embed iframe,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user