diff --git a/sandbox/transient-button.html.example b/sandbox/transient-button.html.example new file mode 100644 index 0000000000..ab9c10e26d --- /dev/null +++ b/sandbox/transient-button.html.example @@ -0,0 +1,102 @@ + + + + + Video.js Sandbox + + + + + + + + + + + + + + + + diff --git a/src/css/components/_transient-button.scss b/src/css/components/_transient-button.scss new file mode 100644 index 0000000000..6adb924c9a --- /dev/null +++ b/src/css/components/_transient-button.scss @@ -0,0 +1,58 @@ +.video-js .vjs-transient-button { + position: absolute; + height: 3em; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(50, 50, 50, 0.5); + cursor: pointer; + opacity: 1; + transition: opacity 1s; +} + +.video-js .vjs-transient-button span { + padding: 0 0.5em; +} + +.video-js .vjs-transient-button.left { + left: 5px; +} + +.video-js .vjs-transient-button.right { + right: 5px; +} + +.video-js .vjs-transient-button.top { + top: 5px; +} + +.video-js .vjs-transient-button.bottom { + bottom: 35px; +} + +.video-js.vjs-layout-large .vjs-transient-button { + height: 5em; + padding: 0 1em; + font-size: 120%; +} + +.video-js.vjs-layout-x-large .vjs-transient-button, +.video-js.vjs-layout-huge .vjs-transient-button { + height: 8em; + padding: 0 1.5em; + + font-size: 150%; +} + +.video-js .vjs-transient-button.top.avoid-title { + top: 80px; +} + +.video-js .vjs-transient-button:hover { + background-color: rgba(50, 50, 50, 0.9); +} + +.video-js.not-hover .vjs-transient-button:not(.force-display), +.video-js.vjs-user-inactive .vjs-transient-button:not(.force-display) { + opacity: 0; +} diff --git a/src/css/video-js.scss b/src/css/video-js.scss index cf20e45241..fcb8388f20 100644 --- a/src/css/video-js.scss +++ b/src/css/video-js.scss @@ -44,6 +44,7 @@ @import "components/captions-settings"; @import "components/title-bar"; @import "components/skip-buttons"; +@import "components/transient-button"; @import "print"; diff --git a/src/js/transient-button.js b/src/js/transient-button.js new file mode 100644 index 0000000000..07f72c3b53 --- /dev/null +++ b/src/js/transient-button.js @@ -0,0 +1,77 @@ +import Button from './button.js'; +import Component from './component.js'; +import {merge} from './utils/obj'; +import * as Dom from './utils/dom.js'; + +// Options type will need to extend button options, because `className`, `clickHandler` may be used +// `position` is redeundant with `className` but allows position to be set without overriding className or vice versa. +const defaults = { + forceTimeout: 4000, + position: 'bottom left' +}; + +/** + * A floating transient button + * + * @extends Button + */ +class TransientButton extends Button { + /** + * + * @param { import('./player').default } player + * + * @param {object} options + */ + constructor(player, options) { + options = merge(defaults, options); + super(player, options); + this.controlText(options.controlText); + this.hide(); + + // When shown, the float button will be visible even if the user is inactive. + // Clear this if there is any interaction. + player.on(['useractive', 'userinactive'], () => { + this.removeClass('force-display'); + }); + } + + buildCSSClass() { + return `vjs-transient-button ${this.options_.position}`; + } + + createEl() { + /** @type HTMLButtonElement */ + const el = Dom.createEl( + 'button', {}, { + type: 'button', + class: this.buildCSSClass() + }, + Dom.createEl('span') + ); + + this.controlTextEl_ = el.querySelector('span'); + + return el; + } + + show() { + super.show(); + this.addClass('force-display'); + this.forceDisplayTimeout = this.player_.setTimeout(() => { + this.removeClass('force-display'); + }, this.options_.forceTimeout); + } + + hide() { + this.removeClass('force-display'); + super.hide(); + } + + dispose() { + this.player_.clearTimeout(this.forceDisplayTimeout); + super.dispose(); + } +} + +Component.registerComponent('TransientButton', TransientButton); +export default TransientButton;