diff options
Diffstat (limited to 'javascript/videojs/src/js/control-bar/progress-control/progress-control.js')
| -rw-r--r-- | javascript/videojs/src/js/control-bar/progress-control/progress-control.js | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/javascript/videojs/src/js/control-bar/progress-control/progress-control.js b/javascript/videojs/src/js/control-bar/progress-control/progress-control.js new file mode 100644 index 0000000..edb39d6 --- /dev/null +++ b/javascript/videojs/src/js/control-bar/progress-control/progress-control.js @@ -0,0 +1,249 @@ +/** + * @file progress-control.js + */ +import Component from '../../component.js'; +import * as Dom from '../../utils/dom.js'; +import {clamp} from '../../utils/num.js'; +import {bind_, throttle, UPDATE_REFRESH_INTERVAL} from '../../utils/fn.js'; +import {silencePromise} from '../../utils/promise'; + +/** @import Player from '../../player' */ + +import './seek-bar.js'; + +/** + * The Progress Control component contains the seek bar, load progress, + * and play progress. + * + * @extends Component + */ +class ProgressControl extends Component { + + /** + * 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.handleMouseMove = throttle(bind_(this, this.handleMouseMove), UPDATE_REFRESH_INTERVAL); + this.throttledHandleMouseSeek = throttle(bind_(this, this.handleMouseSeek), UPDATE_REFRESH_INTERVAL); + this.handleMouseUpHandler_ = (e) => this.handleMouseUp(e); + this.handleMouseDownHandler_ = (e) => this.handleMouseDown(e); + + this.enable(); + } + + /** + * Create the `Component`'s DOM element + * + * @return {Element} + * The element that was created. + */ + createEl() { + return super.createEl('div', { + className: 'vjs-progress-control vjs-control' + }); + } + + /** + * When the mouse moves over the `ProgressControl`, the pointer position + * gets passed down to the `MouseTimeDisplay` component. + * + * @param {Event} event + * The `mousemove` event that caused this function to run. + * + * @listen mousemove + */ + handleMouseMove(event) { + const seekBar = this.getChild('seekBar'); + + if (!seekBar) { + return; + } + + const playProgressBar = seekBar.getChild('playProgressBar'); + const mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay'); + + if (!playProgressBar && !mouseTimeDisplay) { + return; + } + + const seekBarEl = seekBar.el(); + const seekBarRect = Dom.findPosition(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 = clamp(seekBarPoint, 0, 1); + + if (mouseTimeDisplay) { + mouseTimeDisplay.update(seekBarRect, seekBarPoint); + } + + if (playProgressBar) { + playProgressBar.update(seekBarRect, seekBar.getProgress()); + } + + } + + /** + * A throttled version of the {@link ProgressControl#handleMouseSeek} listener. + * + * @method ProgressControl#throttledHandleMouseSeek + * @param {Event} event + * The `mousemove` event that caused this function to run. + * + * @listen mousemove + * @listen touchmove + */ + + /** + * Handle `mousemove` or `touchmove` events on the `ProgressControl`. + * + * @param {Event} event + * `mousedown` or `touchstart` event that triggered this function + * + * @listens mousemove + * @listens touchmove + */ + handleMouseSeek(event) { + const seekBar = this.getChild('seekBar'); + + if (seekBar) { + seekBar.handleMouseMove(event); + } + } + + /** + * Are controls are currently enabled for this progress control. + * + * @return {boolean} + * true if controls are enabled, false otherwise + */ + enabled() { + return this.enabled_; + } + + /** + * Disable all controls on the progress control and its children + */ + disable() { + this.children().forEach((child) => child.disable && child.disable()); + + if (!this.enabled()) { + return; + } + + this.off(['mousedown', 'touchstart'], this.handleMouseDownHandler_); + this.off(this.el_, ['mousemove', 'touchmove'], this.handleMouseMove); + + this.removeListenersAddedOnMousedownAndTouchstart(); + + this.addClass('disabled'); + + this.enabled_ = false; + + // Restore normal playback state if controls are disabled while scrubbing + if (this.player_.scrubbing()) { + const seekBar = this.getChild('seekBar'); + + this.player_.scrubbing(false); + + if (seekBar.videoWasPlaying) { + silencePromise(this.player_.play()); + } + } + } + + /** + * Enable all controls on the progress control and its children + */ + enable() { + this.children().forEach((child) => child.enable && child.enable()); + + if (this.enabled()) { + return; + } + + this.on(['mousedown', 'touchstart'], this.handleMouseDownHandler_); + this.on(this.el_, ['mousemove', 'touchmove'], this.handleMouseMove); + this.removeClass('disabled'); + + this.enabled_ = true; + } + + /** + * Cleanup listeners after the user finishes interacting with the progress controls + */ + removeListenersAddedOnMousedownAndTouchstart() { + const doc = this.el_.ownerDocument; + + this.off(doc, 'mousemove', this.throttledHandleMouseSeek); + this.off(doc, 'touchmove', this.throttledHandleMouseSeek); + this.off(doc, 'mouseup', this.handleMouseUpHandler_); + this.off(doc, 'touchend', this.handleMouseUpHandler_); + } + + /** + * Handle `mousedown` or `touchstart` events on the `ProgressControl`. + * + * @param {Event} event + * `mousedown` or `touchstart` event that triggered this function + * + * @listens mousedown + * @listens touchstart + */ + handleMouseDown(event) { + const doc = this.el_.ownerDocument; + const seekBar = this.getChild('seekBar'); + + if (seekBar) { + seekBar.handleMouseDown(event); + } + + this.on(doc, 'mousemove', this.throttledHandleMouseSeek); + this.on(doc, 'touchmove', this.throttledHandleMouseSeek); + this.on(doc, 'mouseup', this.handleMouseUpHandler_); + this.on(doc, 'touchend', this.handleMouseUpHandler_); + } + + /** + * Handle `mouseup` or `touchend` events on the `ProgressControl`. + * + * @param {Event} event + * `mouseup` or `touchend` event that triggered this function. + * + * @listens touchend + * @listens mouseup + */ + handleMouseUp(event) { + const seekBar = this.getChild('seekBar'); + + if (seekBar) { + seekBar.handleMouseUp(event); + } + + this.removeListenersAddedOnMousedownAndTouchstart(); + } +} + +/** + * Default options for `ProgressControl` + * + * @type {Object} + * @private + */ +ProgressControl.prototype.options_ = { + children: [ + 'seekBar' + ] +}; + +Component.registerComponent('ProgressControl', ProgressControl); +export default ProgressControl; |
