From de97e06a7477dd44bc2286f1968370adf135d8cb Mon Sep 17 00:00:00 2001 From: mister-ben Date: Mon, 22 Aug 2022 18:15:02 +0200 Subject: [PATCH] feat: Use picture el for poster (#7865) BREAKING CHANGE: This changes the DOM structure used for the video poster. --- src/css/components/_poster.scss | 4 -- src/js/player.js | 16 +++++--- src/js/poster-image.js | 69 +++++++++++++++++++++++++++------ test/unit/poster.test.js | 19 +++++++-- 4 files changed, 83 insertions(+), 25 deletions(-) diff --git a/src/css/components/_poster.scss b/src/css/components/_poster.scss index a0d3fe9611..6659a49632 100644 --- a/src/css/components/_poster.scss +++ b/src/css/components/_poster.scss @@ -1,10 +1,6 @@ .vjs-poster { display: inline-block; vertical-align: middle; - background-repeat: no-repeat; - background-position: 50% 50%; - background-size: contain; - background-color: #000000; cursor: pointer; margin: 0; padding: 0; diff --git a/src/js/player.js b/src/js/player.js index 516f124f64..17d414cbba 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -820,25 +820,29 @@ class Player extends Component { * * @see [Video Element Attributes]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-crossorigin} * - * @param {string} [value] + * @param {string|null} [value] * The value to set the `Player`'s crossOrigin to. If an argument is - * given, must be one of `anonymous` or `use-credentials`. + * given, must be one of `'anonymous'` or `'use-credentials'`, or 'null'. * - * @return {string|undefined} + * @return {string|null|undefined} * - The current crossOrigin value of the `Player` when getting. * - undefined when setting */ crossOrigin(value) { - if (!value) { + // `null` can be set to unset a value + if (typeof value === 'undefined') { return this.techGet_('crossOrigin'); } - if (value !== 'anonymous' && value !== 'use-credentials') { - log.warn(`crossOrigin must be "anonymous" or "use-credentials", given "${value}"`); + if (value !== null && value !== 'anonymous' && value !== 'use-credentials') { + log.warn(`crossOrigin must be null, "anonymous" or "use-credentials", given "${value}"`); return; } this.techCall_('setCrossOrigin', value); + if (this.posterImage) { + this.posterImage.crossOrigin(value); + } return; } diff --git a/src/js/poster-image.js b/src/js/poster-image.js index a186d4074b..ad890323d0 100644 --- a/src/js/poster-image.js +++ b/src/js/poster-image.js @@ -46,16 +46,56 @@ class PosterImage extends ClickableComponent { * The element that gets created. */ createEl() { - const el = Dom.createEl('div', { + const el = Dom.createEl('picture', { className: 'vjs-poster', // Don't want poster to be tabbable. tabIndex: -1 - }); + }, {}, Dom.createEl('img', { + loading: 'lazy', + crossOrigin: this.crossOrigin() + })); return el; } + /** + * Get or set the `PosterImage`'s crossOrigin option. + * + * @param {string|null} [value] + * The value to set the crossOrigin to. If an argument is + * given, must be one of `'anonymous'` or `'use-credentials'`, or 'null'. + * + * @return {string|null} + * - The current crossOrigin value of the `Player` when getting. + * - undefined when setting + */ + crossOrigin(value) { + // `null` can be set to unset a value + if (typeof value === 'undefined') { + if (this.el_) { + // If the poster's element exists, give its value + return this.el_.querySelector('img').crossOrigin; + } else if (this.player_.tech_ && this.player_.tech_.isReady_) { + // If not but the tech is ready, query the tech + return this.player_.crossOrigin(); + } + // Otherwise check options as the poster is usually set before the state of crossorigin + // can be retrieved by the getter + return this.player_.options_.crossOrigin || this.player_.options_.crossorigin || null; + + } + + if (value !== null && value !== 'anonymous' && value !== 'use-credentials') { + this.player_.log.warn(`crossOrigin must be null, "anonymous" or "use-credentials", given "${value}"`); + return; + } + + this.el_.querySelector('img').crossOrigin = value; + + return; + } + /** * An {@link EventTarget~EventListener} for {@link Player#posterchange} events. * @@ -85,15 +125,7 @@ class PosterImage extends ClickableComponent { * The URL to the source for the `PosterImage`. */ setSrc(url) { - let backgroundImage = ''; - - // Any falsy value should stay as an empty string, otherwise - // this will throw an extra error - if (url) { - backgroundImage = `url("${url}")`; - } - - this.el_.style.backgroundImage = backgroundImage; + this.el_.querySelector('img').src = url; } /** @@ -126,5 +158,20 @@ class PosterImage extends ClickableComponent { } +/** + * Get or set the `PosterImage`'s crossorigin option. For the HTML5 player, this + * sets the `crossOrigin` property on the `` tag to control the CORS + * behavior. + * + * @param {string|null} [value] + * The value to set the `PosterImages`'s crossorigin to. If an argument is + * given, must be one of `anonymous` or `use-credentials`. + * + * @return {string|null|undefined} + * - The current crossorigin value of the `Player` when getting. + * - undefined when setting + */ +PosterImage.prototype.crossorigin = PosterImage.prototype.crossOrigin; + Component.registerComponent('PosterImage', PosterImage); export default PosterImage; diff --git a/test/unit/poster.test.js b/test/unit/poster.test.js index 7bac08f59a..98e0d5970e 100644 --- a/test/unit/poster.test.js +++ b/test/unit/poster.test.js @@ -18,19 +18,30 @@ QUnit.module('PosterImage', { QUnit.test('should create and update a poster image', function(assert) { const posterImage = new PosterImage(this.mockPlayer); - let backgroundImage = posterImage.el().style.backgroundImage; + let pictureImg = posterImage.$('img').src; - assert.notEqual(backgroundImage.indexOf(this.poster1), -1, 'Background image used'); + assert.notEqual(pictureImg.indexOf(this.poster1), -1, 'Background image used'); // Update with a new poster source and check the new value this.mockPlayer.poster_ = this.poster2; this.mockPlayer.trigger('posterchange'); - backgroundImage = posterImage.el().style.backgroundImage; - assert.notEqual(backgroundImage.indexOf(this.poster2), -1, 'Background image updated'); + pictureImg = posterImage.$('img').src; + assert.notEqual(pictureImg.indexOf(this.poster2), -1, 'Background image updated'); posterImage.dispose(); }); +QUnit.test('should mirror crossOrigin', function(assert) { + assert.strictEqual(this.mockPlayer.posterImage.$('img').crossOrigin, null, 'crossOrigin not set when not present in options'); + assert.strictEqual(this.mockPlayer.posterImage.crossOrigin(), null, 'crossOrigin not set from getter when not present in options'); + + this.mockPlayer.crossOrigin('anonymous'); + + assert.strictEqual(this.mockPlayer.posterImage.$('img').crossOrigin, 'anonymous', 'crossOrigin updated'); + assert.strictEqual(this.mockPlayer.posterImage.crossOrigin(), 'anonymous', 'crossOrigin getter returns updated value'); + +}); + QUnit.test('should remove itself from the document flow when there is no poster', function(assert) { const posterImage = new PosterImage(this.mockPlayer);