Skip to content

Commit

Permalink
feat: Use picture el for poster (videojs#7865)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: This changes the DOM structure used for the video poster.
  • Loading branch information
mister-ben authored and edirub committed Jun 8, 2023
1 parent 2dd68ed commit de97e06
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 25 deletions.
4 changes: 0 additions & 4 deletions src/css/components/_poster.scss
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
16 changes: 10 additions & 6 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
69 changes: 58 additions & 11 deletions src/js/poster-image.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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 `<img>` 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;
19 changes: 15 additions & 4 deletions test/unit/poster.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit de97e06

Please sign in to comment.