From 57cf88bc9fa7b01c1494267157204007123d46f4 Mon Sep 17 00:00:00 2001 From: brandonocasey Date: Fri, 2 Aug 2019 15:28:38 -0400 Subject: [PATCH 1/6] perf: only update on change --- .../progress-control/progress-control.js | 45 +++++---- .../control-bar/progress-control/seek-bar.js | 95 +++++++++---------- .../progress-control/time-tooltip.js | 4 + .../time-controls/current-time-display.js | 40 ++------ .../time-controls/duration-display.js | 7 +- .../time-controls/remaining-time-display.js | 30 ++---- .../control-bar/time-controls/time-display.js | 70 +++++--------- src/js/slider/slider.js | 33 ++----- 8 files changed, 128 insertions(+), 196 deletions(-) diff --git a/src/js/control-bar/progress-control/progress-control.js b/src/js/control-bar/progress-control/progress-control.js index 82f840665d..8b5bdc9f15 100644 --- a/src/js/control-bar/progress-control/progress-control.js +++ b/src/js/control-bar/progress-control/progress-control.js @@ -56,25 +56,34 @@ class ProgressControl extends Component { handleMouseMove(event) { const seekBar = this.getChild('seekBar'); - if (seekBar) { - const mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay'); - const seekBarEl = seekBar.el(); - const seekBarRect = Dom.getBoundingClientRect(seekBarEl); - let seekBarPoint = Dom.getPointerPosition(seekBarEl, event).x; - - // The default skin has a gap on either side of the `SeekBar`. This means - // that it's possible to trigger this behavior outside the boundaries of - // the `SeekBar`. This ensures we stay within it at all times. - if (seekBarPoint > 1) { - seekBarPoint = 1; - } else if (seekBarPoint < 0) { - seekBarPoint = 0; - } - - if (mouseTimeDisplay) { - mouseTimeDisplay.update(seekBarRect, seekBarPoint); - } + if (!seekBar) { + return; } + + const playProgressBar = seekBar.getChild('playProgressBar'); + const mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay'); + + if (!playProgressBar && !mouseTimeDisplay) { + return; + } + + const seekBarEl = seekBar.el(); + const seekBarRect = Dom.getBoundingClientRect(seekBarEl); + let seekBarPoint = Dom.getPointerPosition(seekBarEl, event).x; + + // The default skin has a gap on either side of the `SeekBar`. This means + // that it's possible to trigger this behavior outside the boundaries of + // the `SeekBar`. This ensures we stay within it at all times. + seekBarPoint = Math.max(0, Math.min(1, seekBarPoint)); + + if (mouseTimeDisplay) { + mouseTimeDisplay.update(seekBarRect, seekBarPoint); + } + + if (playProgressBar) { + playProgressBar.update(seekBarRect, seekBar.percent_); + } + } /** diff --git a/src/js/control-bar/progress-control/seek-bar.js b/src/js/control-bar/progress-control/seek-bar.js index ea19ff76a3..8ffc3ca5f0 100644 --- a/src/js/control-bar/progress-control/seek-bar.js +++ b/src/js/control-bar/progress-control/seek-bar.js @@ -54,9 +54,7 @@ class SeekBar extends Slider { setEventHandlers_() { this.update = Fn.throttle(Fn.bind(this, this.update), UPDATE_REFRESH_INTERVAL); - this.on(this.player_, 'timeupdate', this.update); - this.on(this.player_, 'ended', this.handleEnded); - this.on(this.player_, 'durationchange', this.update); + this.on(this.player_, ['ended', 'durationchange', 'timeupdate'], this.update); if (this.player_.liveTracker) { this.on(this.player_.liveTracker, 'liveedgechange', this.update); } @@ -83,16 +81,16 @@ class SeekBar extends Slider { this.enableInterval_(); // we just switched back to the page and someone may be looking, so, update ASAP - this.requestAnimationFrame(this.update); + this.update(); } } enableInterval_() { - this.clearInterval(this.updateInterval); + if (this.updateInterval) { + return; - this.updateInterval = this.setInterval(() =>{ - this.requestAnimationFrame(this.update); - }, UPDATE_REFRESH_INTERVAL); + } + this.updateInterval = this.setInterval(this.update, UPDATE_REFRESH_INTERVAL); } disableInterval_(e) { @@ -100,7 +98,12 @@ class SeekBar extends Slider { return; } + if (!this.updateInterval) { + return; + } + this.clearInterval(this.updateInterval); + this.updateInterval = null; } /** @@ -130,31 +133,36 @@ class SeekBar extends Slider { * @private */ update_(currentTime, percent) { - const liveTracker = this.player_.liveTracker; - let duration = this.player_.duration(); + this.requestAnimationFrame(() => { + const liveTracker = this.player_.liveTracker; + let duration = this.player_.duration(); - if (liveTracker && liveTracker.isLive()) { - duration = this.player_.liveTracker.liveCurrentTime(); - } + if (liveTracker && liveTracker.isLive()) { + duration = this.player_.liveTracker.liveCurrentTime(); + } - // machine readable value of progress bar (percentage complete) - this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2)); - - // human readable value of progress bar (time complete) - this.el_.setAttribute( - 'aria-valuetext', - this.localize( - 'progress bar timing: currentTime={1} duration={2}', - [formatTime(currentTime, duration), - formatTime(duration, duration)], - '{1} of {2}' - ) - ); - - // Update the `PlayProgressBar`. - if (this.bar) { - this.bar.update(Dom.getBoundingClientRect(this.el_), percent); - } + if (this.percent_ !== percent) { + // machine readable value of progress bar (percentage complete) + this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2)); + this.percent_ = percent; + } + + if (this.currentTime_ !== currentTime || this.duration_ !== duration) { + // human readable value of progress bar (time complete) + this.el_.setAttribute( + 'aria-valuetext', + this.localize( + 'progress bar timing: currentTime={1} duration={2}', + [formatTime(currentTime, duration), + formatTime(duration, duration)], + '{1} of {2}' + ) + ); + + this.currentTime_ = currentTime; + this.duration_ = duration; + } + }); } /** @@ -169,15 +177,11 @@ class SeekBar extends Slider { * The current percent at a number from 0-1 */ update(event) { - // if the offsetParent is null, then this element is hidden, in which case - // we don't need to update it. - if (this.el().offsetParent === null) { - return; - } - const percent = super.update(); + const currentTime = this.player_.ended() ? + this.player.duration() : this.getCurrentTime_(); - this.update_(this.getCurrentTime_(), percent); + this.update_(currentTime, percent); return percent; } @@ -196,19 +200,6 @@ class SeekBar extends Slider { this.player_.currentTime(); } - /** - * We want the seek bar to be full on ended - * no matter what the actual internal values are. so we force it. - * - * @param {EventTarget~Event} [event] - * The `timeupdate` or `ended` event that caused this to run. - * - * @listens Player#ended - */ - handleEnded(event) { - this.update_(this.player_.duration(), 1); - } - /** * Get the percentage of media played so far. * @@ -231,7 +222,7 @@ class SeekBar extends Slider { percent = currentTime / this.player_.duration(); } - return percent >= 1 ? 1 : (percent || 0); + return percent; } /** diff --git a/src/js/control-bar/progress-control/time-tooltip.js b/src/js/control-bar/progress-control/time-tooltip.js index e336fdc0b5..9da2fb7b7b 100644 --- a/src/js/control-bar/progress-control/time-tooltip.js +++ b/src/js/control-bar/progress-control/time-tooltip.js @@ -5,6 +5,7 @@ import Component from '../../component'; import * as Dom from '../../utils/dom.js'; import formatTime from '../../utils/format-time.js'; import * as Fn from '../../utils/fn.js'; +import * as browser from '../../utils/browser.js'; /** * Time tooltips display a time above the progress bar. @@ -24,6 +25,9 @@ class TimeTooltip extends Component { */ constructor(player, options) { super(player, options); + if (browser.IS_IOS || browser.IS_ANDROID) { + this.hide(); + } this.update = Fn.throttle(Fn.bind(this, this.update), Fn.UPDATE_REFRESH_INTERVAL); } diff --git a/src/js/control-bar/time-controls/current-time-display.js b/src/js/control-bar/time-controls/current-time-display.js index f2fdea203d..3eec4b1556 100644 --- a/src/js/control-bar/time-controls/current-time-display.js +++ b/src/js/control-bar/time-controls/current-time-display.js @@ -11,20 +11,6 @@ import Component from '../../component.js'; */ class CurrentTimeDisplay extends TimeDisplay { - /** - * Creates an instance of this class. - * - * @param {Player} player - * The `Player` that this class should be attached to. - * - * @param {Object} [options] - * The key/value store of player options. - */ - constructor(player, options) { - super(player, options); - this.on(player, 'ended', this.handleEnded); - } - /** * Builds the default DOM `className`. * @@ -45,28 +31,16 @@ class CurrentTimeDisplay extends TimeDisplay { */ updateContent(event) { // Allows for smooth scrubbing, when player can't keep up. - const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); - - this.updateFormattedTime_(time); - } + let time; - /** - * When the player fires ended there should be no time left. Sadly - * this is not always the case, lets make it seem like that is the case - * for users. - * - * @param {EventTarget~Event} [event] - * The `ended` event that caused this to run. - * - * @listens Player#ended - */ - handleEnded(event) { - if (!this.player_.duration()) { - return; + if (this.player_.ended()) { + time = this.player_.duration(); + } else { + time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); } - this.updateFormattedTime_(this.player_.duration()); - } + this.updateTextNode_(time); + } } /** diff --git a/src/js/control-bar/time-controls/duration-display.js b/src/js/control-bar/time-controls/duration-display.js index f8c24b155d..63af3fe6ab 100644 --- a/src/js/control-bar/time-controls/duration-display.js +++ b/src/js/control-bar/time-controls/duration-display.js @@ -36,7 +36,7 @@ class DurationDisplay extends TimeDisplay { // Also listen for timeupdate (in the parent) and loadedmetadata because removing those // listeners could have broken dependent applications/libraries. These // can likely be removed for 7.0. - this.on(player, 'loadedmetadata', this.throttledUpdateContent); + this.on(player, 'loadedmetadata', this.updateContent); } /** @@ -63,10 +63,7 @@ class DurationDisplay extends TimeDisplay { updateContent(event) { const duration = this.player_.duration(); - if (this.duration_ !== duration) { - this.duration_ = duration; - this.updateFormattedTime_(duration); - } + this.updateTextNode_(duration); } } diff --git a/src/js/control-bar/time-controls/remaining-time-display.js b/src/js/control-bar/time-controls/remaining-time-display.js index 68294dbf25..77995a07c5 100644 --- a/src/js/control-bar/time-controls/remaining-time-display.js +++ b/src/js/control-bar/time-controls/remaining-time-display.js @@ -23,8 +23,7 @@ class RemainingTimeDisplay extends TimeDisplay { */ constructor(player, options) { super(player, options); - this.on(player, 'durationchange', this.throttledUpdateContent); - this.on(player, 'ended', this.handleEnded); + this.on(player, 'durationchange', this.updateContent); } /** @@ -64,30 +63,19 @@ class RemainingTimeDisplay extends TimeDisplay { return; } + let time; + // @deprecated We should only use remainingTimeDisplay // as of video.js 7 - if (this.player_.remainingTimeDisplay) { - this.updateFormattedTime_(this.player_.remainingTimeDisplay()); + if (this.player_.ended()) { + time = 0; + } else if (this.player_.remainingTimeDisplay) { + time = this.player_.remainingTimeDisplay(); } else { - this.updateFormattedTime_(this.player_.remainingTime()); + time = this.player_.remainingTime(); } - } - /** - * When the player fires ended there should be no time left. Sadly - * this is not always the case, lets make it seem like that is the case - * for users. - * - * @param {EventTarget~Event} [event] - * The `ended` event that caused this to run. - * - * @listens Player#ended - */ - handleEnded(event) { - if (!this.player_.duration()) { - return; - } - this.updateFormattedTime_(0); + this.updateTextNode_(time); } } diff --git a/src/js/control-bar/time-controls/time-display.js b/src/js/control-bar/time-controls/time-display.js index 6440408472..e104c8d0b6 100644 --- a/src/js/control-bar/time-controls/time-display.js +++ b/src/js/control-bar/time-controls/time-display.js @@ -4,7 +4,6 @@ import document from 'global/document'; import Component from '../../component.js'; import * as Dom from '../../utils/dom.js'; -import {bind, throttle, UPDATE_REFRESH_INTERVAL} from '../../utils/fn.js'; import formatTime from '../../utils/format-time.js'; /** @@ -25,8 +24,9 @@ class TimeDisplay extends Component { */ constructor(player, options) { super(player, options); - this.throttledUpdateContent = throttle(bind(this, this.updateContent), UPDATE_REFRESH_INTERVAL); - this.on(player, 'timeupdate', this.throttledUpdateContent); + + this.on(player, ['timeupdate', 'ended'], this.updateContent); + this.updateTextNode_(); } /** @@ -54,7 +54,6 @@ class TimeDisplay extends Component { 'role': 'presentation' }); - this.updateTextNode_(); el.appendChild(this.contentEl_); return el; } @@ -67,57 +66,40 @@ class TimeDisplay extends Component { } /** - * Updates the "remaining time" text node with new content using the - * contents of the `formattedTime_` property. + * Updates the time display text node with a new time + * + * @param {number} [time=0] the time to update to * * @private */ - updateTextNode_() { - if (!this.contentEl_) { + updateTextNode_(time = 0) { + time = formatTime(time); + + if (this.formattedTime_ === time) { return; } - while (this.contentEl_.firstChild) { - this.contentEl_.removeChild(this.contentEl_.firstChild); - } + this.formattedTime_ = time; - this.textNode_ = document.createTextNode(this.formattedTime_ || this.formatTime_(0)); - this.contentEl_.appendChild(this.textNode_); - } + this.requestAnimationFrame(() => { + if (!this.contentEl_) { + return; + } - /** - * Generates a formatted time for this component to use in display. - * - * @param {number} time - * A numeric time, in seconds. - * - * @return {string} - * A formatted time - * - * @private - */ - formatTime_(time) { - return formatTime(time); - } + const oldNode = this.textNode_; - /** - * Updates the time display text node if it has what was passed in changed - * the formatted time. - * - * @param {number} time - * The time to update to - * - * @private - */ - updateFormattedTime_(time) { - const formattedTime = this.formatTime_(time); + this.textNode_ = document.createTextNode(this.formattedTime_); - if (formattedTime === this.formattedTime_) { - return; - } + if (!this.textNode_) { + return; + } - this.formattedTime_ = formattedTime; - this.requestAnimationFrame(this.updateTextNode_); + if (oldNode) { + this.contentEl_.replaceChild(this.textNode_, oldNode); + } else { + this.contentEl_.appendChild(this.textNode_); + } + }); } /** diff --git a/src/js/slider/slider.js b/src/js/slider/slider.js index cac295eab3..61f53d6356 100644 --- a/src/js/slider/slider.js +++ b/src/js/slider/slider.js @@ -234,40 +234,27 @@ class Slider extends Component { // In VolumeBar init we have a setTimeout for update that pops and update // to the end of the execution stack. The player is destroyed before then // update will cause an error - if (!this.el_) { + // If there's no bar... + if (!this.el_ || !this.bar) { return; } - // If scrubbing, we could use a cached value to make the handle keep up - // with the user's mouse. On HTML5 browsers scrubbing is really smooth, but - // some flash players are slow, so we might want to utilize this later. - // var progress = (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration(); - let progress = this.getPercent(); - const bar = this.bar; + // clamp progress between 0 and 1 + // and only round to four decimal places, as we round to two below + const percent = Number(this.getPercent()) || 0; + const progress = Math.max(0, Math.min(1, percent)).toFixed(4); - // If there's no bar... - if (!bar) { + if (progress === this.progress_) { return; } - // Protect against no duration and other division issues - if (typeof progress !== 'number' || - progress !== progress || - progress < 0 || - progress === Infinity) { - progress = 0; - } - - // Convert to a percentage for setting - const percentage = (progress * 100).toFixed(2) + '%'; - const style = bar.el().style; + this.progress_ = progress; // Set the new bar width or height const sizeKey = this.vertical() ? 'height' : 'width'; - if (style[sizeKey] !== percentage) { - style[sizeKey] = percentage; - } + // Convert to a percentage for css value + this.bar.el().style[sizeKey] = (progress * 100).toFixed(2) + '%'; return progress; } From dd2e47834294be862c00b2dcc86c441412e45e25 Mon Sep 17 00:00:00 2001 From: brandonocasey Date: Fri, 2 Aug 2019 15:40:47 -0400 Subject: [PATCH 2/6] add clamp helper --- .../progress-control/progress-control.js | 5 +-- .../control-bar/progress-control/seek-bar.js | 34 ++++++------------- .../progress-control/time-tooltip.js | 4 --- src/js/slider/slider.js | 26 ++++++++++---- src/js/utils/clamp.js | 19 +++++++++++ 5 files changed, 51 insertions(+), 37 deletions(-) create mode 100644 src/js/utils/clamp.js diff --git a/src/js/control-bar/progress-control/progress-control.js b/src/js/control-bar/progress-control/progress-control.js index 8b5bdc9f15..63536b1988 100644 --- a/src/js/control-bar/progress-control/progress-control.js +++ b/src/js/control-bar/progress-control/progress-control.js @@ -3,6 +3,7 @@ */ import Component from '../../component.js'; import * as Dom from '../../utils/dom.js'; +import clamp from '../../utils/clamp.js'; import {bind, throttle, UPDATE_REFRESH_INTERVAL} from '../../utils/fn.js'; import './seek-bar.js'; @@ -74,14 +75,14 @@ class ProgressControl extends Component { // The default skin has a gap on either side of the `SeekBar`. This means // that it's possible to trigger this behavior outside the boundaries of // the `SeekBar`. This ensures we stay within it at all times. - seekBarPoint = Math.max(0, Math.min(1, seekBarPoint)); + seekBarPoint = clamp(0, 1, seekBarPoint); if (mouseTimeDisplay) { mouseTimeDisplay.update(seekBarRect, seekBarPoint); } if (playProgressBar) { - playProgressBar.update(seekBarRect, seekBar.percent_); + playProgressBar.update(seekBarRect, seekBar.getProgress()); } } diff --git a/src/js/control-bar/progress-control/seek-bar.js b/src/js/control-bar/progress-control/seek-bar.js index 8ffc3ca5f0..5239e0753b 100644 --- a/src/js/control-bar/progress-control/seek-bar.js +++ b/src/js/control-bar/progress-control/seek-bar.js @@ -124,16 +124,20 @@ class SeekBar extends Slider { * This function updates the play progress bar and accessibility * attributes to whatever is passed in. * - * @param {number} currentTime - * The currentTime value that should be used for accessibility + * @param {EventTarget~Event} [event] + * The `timeupdate` or `ended` event that caused this to run. * - * @param {number} percent - * The percentage as a decimal that the bar should be filled from 0-1. + * @listens Player#timeupdate * - * @private + * @return {number} + * The current percent at a number from 0-1 */ - update_(currentTime, percent) { + update(event) { + const percent = super.update(); + this.requestAnimationFrame(() => { + const currentTime = this.player_.ended() ? + this.player.duration() : this.getCurrentTime_(); const liveTracker = this.player_.liveTracker; let duration = this.player_.duration(); @@ -163,25 +167,7 @@ class SeekBar extends Slider { this.duration_ = duration; } }); - } - - /** - * Update the seek bar's UI. - * - * @param {EventTarget~Event} [event] - * The `timeupdate` or `ended` event that caused this to run. - * - * @listens Player#timeupdate - * - * @return {number} - * The current percent at a number from 0-1 - */ - update(event) { - const percent = super.update(); - const currentTime = this.player_.ended() ? - this.player.duration() : this.getCurrentTime_(); - this.update_(currentTime, percent); return percent; } diff --git a/src/js/control-bar/progress-control/time-tooltip.js b/src/js/control-bar/progress-control/time-tooltip.js index 9da2fb7b7b..e336fdc0b5 100644 --- a/src/js/control-bar/progress-control/time-tooltip.js +++ b/src/js/control-bar/progress-control/time-tooltip.js @@ -5,7 +5,6 @@ import Component from '../../component'; import * as Dom from '../../utils/dom.js'; import formatTime from '../../utils/format-time.js'; import * as Fn from '../../utils/fn.js'; -import * as browser from '../../utils/browser.js'; /** * Time tooltips display a time above the progress bar. @@ -25,9 +24,6 @@ class TimeTooltip extends Component { */ constructor(player, options) { super(player, options); - if (browser.IS_IOS || browser.IS_ANDROID) { - this.hide(); - } this.update = Fn.throttle(Fn.bind(this, this.update), Fn.UPDATE_REFRESH_INTERVAL); } diff --git a/src/js/slider/slider.js b/src/js/slider/slider.js index 61f53d6356..b33e9f9dbf 100644 --- a/src/js/slider/slider.js +++ b/src/js/slider/slider.js @@ -5,6 +5,7 @@ import Component from '../component.js'; import * as Dom from '../utils/dom.js'; import {assign} from '../utils/obj'; import {IS_CHROME} from '../utils/browser.js'; +import clamp from '../utils/clamp.js'; import keycode from 'keycode'; /** @@ -230,7 +231,6 @@ class Slider extends Component { * number from 0 to 1. */ update() { - // In VolumeBar init we have a setTimeout for update that pops and update // to the end of the execution stack. The player is destroyed before then // update will cause an error @@ -241,8 +241,7 @@ class Slider extends Component { // clamp progress between 0 and 1 // and only round to four decimal places, as we round to two below - const percent = Number(this.getPercent()) || 0; - const progress = Math.max(0, Math.min(1, percent)).toFixed(4); + const progress = this.getProgress(); if (progress === this.progress_) { return; @@ -250,15 +249,28 @@ class Slider extends Component { this.progress_ = progress; - // Set the new bar width or height - const sizeKey = this.vertical() ? 'height' : 'width'; + this.requestAnimationFrame(() => { + // Set the new bar width or height + const sizeKey = this.vertical() ? 'height' : 'width'; - // Convert to a percentage for css value - this.bar.el().style[sizeKey] = (progress * 100).toFixed(2) + '%'; + // Convert to a percentage for css value + this.bar.el().style[sizeKey] = (progress * 100).toFixed(2) + '%'; + }); return progress; } + /** + * Get the percentage of the bar that should be filled + * but clamped and rounded. + * + * @return {number} + * percentage filled that the slider is + */ + getProgress() { + return clamp(this.getPercent(), 0, 1).toFixed(4); + } + /** * Calculate distance for slider * diff --git a/src/js/utils/clamp.js b/src/js/utils/clamp.js new file mode 100644 index 0000000000..057a64cd0d --- /dev/null +++ b/src/js/utils/clamp.js @@ -0,0 +1,19 @@ +/** + * Keep a number between a min and a max value + * + * @param {number} number + * The number to clamp + * + * @param {number} min + * The minimum value + * @param {number} max + * The maximum value + * + * @return {number} + * the clamped number + */ +const clamp = function(number, min, max) { + return Math.min(max, Math.max(min, Number(number))); +}; + +export default clamp; From f07a341a2fe3568ff11e0aa6c7234c6d02c5a1c4 Mon Sep 17 00:00:00 2001 From: brandonocasey Date: Fri, 2 Aug 2019 16:04:40 -0400 Subject: [PATCH 3/6] update load-progress-bar too --- .../progress-control/load-progress-bar.js | 103 ++++++++++-------- 1 file changed, 59 insertions(+), 44 deletions(-) diff --git a/src/js/control-bar/progress-control/load-progress-bar.js b/src/js/control-bar/progress-control/load-progress-bar.js index 019d757c5d..2ef3446ba6 100644 --- a/src/js/control-bar/progress-control/load-progress-bar.js +++ b/src/js/control-bar/progress-control/load-progress-bar.js @@ -3,6 +3,11 @@ */ import Component from '../../component.js'; import * as Dom from '../../utils/dom.js'; +import clamp from '../../utils/clamp'; +import document from 'global/document'; + +// get the percent width of a time compared to the total end +const percentify = (time, end) => clamp((time / end) * 100, 0, 100).toFixed(2) + '%'; /** * Shows loading progress @@ -33,14 +38,27 @@ class LoadProgressBar extends Component { * The element that was created. */ createEl() { - return super.createEl('div', { - className: 'vjs-load-progress', - innerHTML: `${this.localize('Loaded')}: 0%` + const el = super.createEl('div', {className: 'vjs-load-progress'}); + const wrapper = super.createEl('span', {className: 'vjs-control-text'}); + const loadedText = super.createEl('span', {textContent: this.localize('Loaded')}); + const separator = document.createTextNode(': '); + + this.percentageEl_ = super.createEl('span', { + className: 'vjs-control-text-loaded-percentage', + textContent: '0%' }); + + el.appendChild(wrapper); + wrapper.appendChild(loadedText); + wrapper.appendChild(separator); + wrapper.appendChild(this.percentageEl_); + + return el; } dispose() { this.partEls_ = null; + this.percentageEl_ = null; super.dispose(); } @@ -54,56 +72,53 @@ class LoadProgressBar extends Component { * @listens Player#progress */ update(event) { - const liveTracker = this.player_.liveTracker; - const buffered = this.player_.buffered(); - const duration = (liveTracker && liveTracker.isLive()) ? liveTracker.seekableEnd() : this.player_.duration(); - const bufferedEnd = this.player_.bufferedEnd(); - const children = this.partEls_; - const controlTextPercentage = this.$('.vjs-control-text-loaded-percentage'); - - // get the percent width of a time compared to the total end - const percentify = function(time, end, rounded) { - // no NaN - let percent = (time / end) || 0; - - percent = (percent >= 1 ? 1 : percent) * 100; - - if (rounded) { - percent = percent.toFixed(2); + this.requestAnimationFrame(() => { + const liveTracker = this.player_.liveTracker; + const buffered = this.player_.buffered(); + const duration = (liveTracker && liveTracker.isLive()) ? liveTracker.seekableEnd() : this.player_.duration(); + const bufferedEnd = this.player_.bufferedEnd(); + const children = this.partEls_; + const percent = percentify(bufferedEnd, duration); + + if (this.percent_ !== percent) { + // update the width of the progress bar + this.el_.style.width = percent; + // update the control-text + Dom.textContent(this.percentageEl_, percent); + this.percent_ = percent; } - return percent + '%'; - }; + // add child elements to represent the individual buffered time ranges + for (let i = 0; i < buffered.length; i++) { + const start = buffered.start(i); + const end = buffered.end(i); + let part = children[i]; - // update the width of the progress bar - this.el_.style.width = percentify(bufferedEnd, duration); + if (!part) { + part = this.el_.appendChild(Dom.createEl()); + children[i] = part; + } - // update the control-text - Dom.textContent(controlTextPercentage, percentify(bufferedEnd, duration, true)); + // only update if changed + if (part.start_ === start && part.end_ === end) { + continue; + } - // add child elements to represent the individual buffered time ranges - for (let i = 0; i < buffered.length; i++) { - const start = buffered.start(i); - const end = buffered.end(i); - let part = children[i]; + part.start_ = start; + part.end_ = end; - if (!part) { - part = this.el_.appendChild(Dom.createEl()); - children[i] = part; + // set the percent based on the width of the progress bar (bufferedEnd) + part.style.left = percentify(start, bufferedEnd); + part.style.width = percentify(end - start, bufferedEnd); } - // set the percent based on the width of the progress bar (bufferedEnd) - part.style.left = percentify(start, bufferedEnd); - part.style.width = percentify(end - start, bufferedEnd); - } - - // remove unused buffered range elements - for (let i = children.length; i > buffered.length; i--) { - this.el_.removeChild(children[i - 1]); - } - children.length = buffered.length; + // remove unused buffered range elements + for (let i = children.length; i > buffered.length; i--) { + this.el_.removeChild(children[i - 1]); + } + children.length = buffered.length; + }); } - } Component.registerComponent('LoadProgressBar', LoadProgressBar); From baf4c237cefc1defa18c72d563c72579b491fc24 Mon Sep 17 00:00:00 2001 From: brandonocasey Date: Fri, 2 Aug 2019 16:30:32 -0400 Subject: [PATCH 4/6] add ended to test faker to fix tests --- src/js/control-bar/progress-control/seek-bar.js | 2 +- test/unit/tech/tech-faker.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/control-bar/progress-control/seek-bar.js b/src/js/control-bar/progress-control/seek-bar.js index 5239e0753b..28e52771d5 100644 --- a/src/js/control-bar/progress-control/seek-bar.js +++ b/src/js/control-bar/progress-control/seek-bar.js @@ -137,7 +137,7 @@ class SeekBar extends Slider { this.requestAnimationFrame(() => { const currentTime = this.player_.ended() ? - this.player.duration() : this.getCurrentTime_(); + this.player_.duration() : this.getCurrentTime_(); const liveTracker = this.player_.liveTracker; let duration = this.player_.duration(); diff --git a/test/unit/tech/tech-faker.js b/test/unit/tech/tech-faker.js index 0606b5d468..7c6891a635 100644 --- a/test/unit/tech/tech-faker.js +++ b/test/unit/tech/tech-faker.js @@ -152,6 +152,9 @@ class TechFaker extends Tech { controls() { return false; } + ended() { + return false; + } // Support everything except for "video/unsupported-format" static isSupported() { From d52c247cca4f7f3c353d0c1f8eb75809e397a789 Mon Sep 17 00:00:00 2001 From: brandonocasey Date: Mon, 5 Aug 2019 17:48:25 -0400 Subject: [PATCH 5/6] cr --- .../progress-control/load-progress-bar.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/js/control-bar/progress-control/load-progress-bar.js b/src/js/control-bar/progress-control/load-progress-bar.js index 2ef3446ba6..59e357739d 100644 --- a/src/js/control-bar/progress-control/load-progress-bar.js +++ b/src/js/control-bar/progress-control/load-progress-bar.js @@ -39,11 +39,11 @@ class LoadProgressBar extends Component { */ createEl() { const el = super.createEl('div', {className: 'vjs-load-progress'}); - const wrapper = super.createEl('span', {className: 'vjs-control-text'}); - const loadedText = super.createEl('span', {textContent: this.localize('Loaded')}); + const wrapper = Dom.createEl('span', {className: 'vjs-control-text'}); + const loadedText = Dom.createEl('span', {textContent: this.localize('Loaded')}); const separator = document.createTextNode(': '); - this.percentageEl_ = super.createEl('span', { + this.percentageEl_ = Dom.createEl('span', { className: 'vjs-control-text-loaded-percentage', textContent: '0%' }); @@ -100,12 +100,12 @@ class LoadProgressBar extends Component { } // only update if changed - if (part.start_ === start && part.end_ === end) { + if (part.dataset.start === start && part.dataset.end === end) { continue; } - part.start_ = start; - part.end_ = end; + part.dataset.start = start; + part.dataset.end = end; // set the percent based on the width of the progress bar (bufferedEnd) part.style.left = percentify(start, bufferedEnd); From c7daea52ff2c8480c4465aa0dc119c3971108147 Mon Sep 17 00:00:00 2001 From: brandonocasey Date: Mon, 5 Aug 2019 23:40:35 -0400 Subject: [PATCH 6/6] bug in clamp --- src/js/utils/clamp.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/utils/clamp.js b/src/js/utils/clamp.js index 057a64cd0d..f4b1e52ee0 100644 --- a/src/js/utils/clamp.js +++ b/src/js/utils/clamp.js @@ -13,7 +13,9 @@ * the clamped number */ const clamp = function(number, min, max) { - return Math.min(max, Math.max(min, Number(number))); + number = Number(number); + + return Math.min(max, Math.max(min, isNaN(number) ? min : number)); }; export default clamp;