diff --git a/src/js/tracks/text-track-display.js b/src/js/tracks/text-track-display.js index c16350aead..04b8ca0e6c 100644 --- a/src/js/tracks/text-track-display.js +++ b/src/js/tracks/text-track-display.js @@ -77,6 +77,21 @@ function tryUpdateStyle(el, style, rule) { } } +/** + * Converts the CSS top/right/bottom/left property numeric value to string in pixels. + * + * @param {number} position + * The CSS top/right/bottom/left property value. + * + * @return {string} + * The CSS property value that was created, like '10px'. + * + * @private + */ +function getCSSPositionValue(position) { + return position ? `${position}px` : ''; +} + /** * The component for displaying text track cues. * @@ -99,11 +114,18 @@ class TextTrackDisplay extends Component { constructor(player, options, ready) { super(player, options, ready); - const updateDisplayHandler = (e) => this.updateDisplay(e); + const updateDisplayTextHandler = (e) => this.updateDisplay(e); + const updateDisplayHandler = (e) => { + this.updateDisplayOverlay(); + this.updateDisplay(e); + }; player.on('loadstart', (e) => this.toggleDisplay(e)); - player.on('texttrackchange', updateDisplayHandler); - player.on('loadedmetadata', (e) => this.preselectTrack(e)); + player.on('texttrackchange', updateDisplayTextHandler); + player.on('loadedmetadata', (e) => { + this.updateDisplayOverlay(); + this.preselectTrack(e); + }); // This used to be called during player init, but was causing an error // if a track should show by default and the display hadn't loaded yet. @@ -297,6 +319,34 @@ class TextTrackDisplay extends Component { } } + /** + * Updates the displayed TextTrack to be sure it overlays the video when a either + * a {@link Player#texttrackchange} or a {@link Player#fullscreenchange} is fired. + */ + updateDisplayOverlay() { + if (!this.player_.videoHeight()) { + return; + } + + const playerWidth = this.player_.currentWidth(); + const playerHeight = this.player_.currentHeight(); + const playerAspectRatio = playerWidth / playerHeight; + const videoAspectRatio = this.player_.videoWidth() / this.player_.videoHeight(); + let insetInlineMatch = 0; + let insetBlockMatch = 0; + + if (Math.abs(playerAspectRatio - videoAspectRatio) > 0.1) { + if (playerAspectRatio > videoAspectRatio) { + insetInlineMatch = Math.round((playerWidth - playerHeight * videoAspectRatio) / 2); + } else { + insetBlockMatch = Math.round((playerHeight - playerWidth / videoAspectRatio) / 2); + } + } + + tryUpdateStyle(this.el_, 'insetInline', getCSSPositionValue(insetInlineMatch)); + tryUpdateStyle(this.el_, 'insetBlock', getCSSPositionValue(insetBlockMatch)); + } + /** * Style {@Link TextTrack} activeCues according to {@Link TextTrackSettings}. * diff --git a/test/unit/tracks/text-track-display.test.js b/test/unit/tracks/text-track-display.test.js index 15e8544508..7b8a1dd9f3 100644 --- a/test/unit/tracks/text-track-display.test.js +++ b/test/unit/tracks/text-track-display.test.js @@ -449,4 +449,53 @@ if (!Html5.supportsNativeTextTracks()) { 'colors must be valid hex codes.' ); }); + + QUnit.test('text track display should overlay a video', function(assert) { + const tag = document.createElement('video'); + + tag.width = 320; + tag.height = 180; + const player = TestHelpers.makePlayer({}, tag); + const textTrackDisplay = player.getChild('TextTrackDisplay'); + const textTrackDisplayStyle = textTrackDisplay.el().style; + + assert.ok(textTrackDisplayStyle.insetInline === '', 'text track display style insetInline equal to empty string'); + assert.ok(textTrackDisplayStyle.insetBlock === '', 'text track display style insetBlock equal to empty string'); + + // video aspect ratio equal to NaN + player.tech_.videoWidth = () => 0; + player.tech_.videoHeight = () => 0; + + assert.ok(textTrackDisplayStyle.insetInline === '', 'text track display style insetInline equal to empty string'); + assert.ok(textTrackDisplayStyle.insetBlock === '', 'text track display style insetBlock equal to empty string'); + + // video aspect ratio 2:1 + player.tech_.videoWidth = () => 100; + player.tech_.videoHeight = () => 50; + + textTrackDisplay.updateDisplayOverlay(); + + assert.ok(textTrackDisplayStyle.insetInline === '', 'text track display style insetInline equal to empty string'); + assert.ok(textTrackDisplayStyle.insetBlock === '10px', 'text track display style insetBlock equal to 10px'); + + // video aspect ratio 4:3 + player.tech_.videoWidth = () => 100; + player.tech_.videoHeight = () => 75; + + textTrackDisplay.updateDisplayOverlay(); + + assert.ok(textTrackDisplayStyle.insetInline === '40px', 'text track display style insetInline equal to 40px'); + assert.ok(textTrackDisplayStyle.insetBlock === '', 'text track display style insetBlock equal to empty string'); + + // video aspect ratio 16:9 + player.tech_.videoWidth = () => 320; + player.tech_.videoHeight = () => 180; + + textTrackDisplay.updateDisplayOverlay(); + + assert.ok(textTrackDisplayStyle.insetInline === '', 'text track display style insetInline equal to empty string'); + assert.ok(textTrackDisplayStyle.insetBlock === '', 'text track display style insetBlock equal to empty string'); + + player.dispose(); + }); }