mirror of
https://github.com/videojs/video.js.git
synced 2024-12-02 09:11:54 +02:00
feat: Add a mouse volume tooltip (#6824)
This commit is contained in:
parent
239c9a1552
commit
b2edfd24ac
@ -138,6 +138,7 @@
|
||||
&:before {
|
||||
position: absolute;
|
||||
font-size: 0.9em; // Doing this to match the handle on play progress.
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +149,7 @@
|
||||
&:before {
|
||||
top: -0.5em;
|
||||
left: -0.3em;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.vjs-slider-horizontal .vjs-volume-level {
|
||||
@ -184,3 +186,83 @@
|
||||
.video-js .vjs-volume-horizontal .vjs-menu {
|
||||
left: -2em;
|
||||
}
|
||||
|
||||
// .vjs-volume-tooltip
|
||||
//
|
||||
// These elements are displayed above the volume bar.
|
||||
//
|
||||
// By default, they are hidden and only shown when hovering over the volume
|
||||
// control.
|
||||
.video-js .vjs-volume-tooltip {
|
||||
@include background-color-with-alpha(#fff, 0.8);
|
||||
@include border-radius(0.3em);
|
||||
color: #000;
|
||||
float: right;
|
||||
font-family: $text-font-family;
|
||||
font-size: 1em;
|
||||
padding: 6px 8px 8px 8px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: -3.4em;
|
||||
visibility: hidden;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-control:hover .vjs-volume-tooltip,
|
||||
.video-js .vjs-volume-control:hover .vjs-progress-holder:focus .vjs-volume-tooltip {
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-vertical:hover .vjs-volume-tooltip,
|
||||
.video-js .vjs-volume-vertical:hover .vjs-progress-holder:focus .vjs-volume-tooltip {
|
||||
left: 1em;
|
||||
top: -12px;
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-control.disabled:hover .vjs-volume-tooltip {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
// .vjs-mouse-display / MouseVolumeLevelDisplay
|
||||
//
|
||||
// This element tracks the mouse position along the volume control and
|
||||
// includes a tooltip, which displays the volume level.
|
||||
.video-js .vjs-volume-control .vjs-mouse-display {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #000;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-horizontal .vjs-mouse-display {
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vjs-no-flex .vjs-volume-control .vjs-mouse-display {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-control:hover .vjs-mouse-display {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.video-js.vjs-user-inactive .vjs-volume-control .vjs-mouse-display {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
$trans: visibility 1.0s, opacity 1.0s;
|
||||
@include transition($trans);
|
||||
}
|
||||
|
||||
.video-js.vjs-user-inactive.vjs-no-flex .vjs-volume-control .vjs-mouse-display {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vjs-mouse-display .vjs-volume-tooltip {
|
||||
color: #fff;
|
||||
@include background-color-with-alpha(#000, 0.8);
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @file mouse-volume-level-display.js
|
||||
*/
|
||||
import Component from '../../component.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
|
||||
import './volume-level-tooltip';
|
||||
|
||||
/**
|
||||
* The {@link MouseVolumeLevelDisplay} component tracks mouse movement over the
|
||||
* {@link VolumeControl}. It displays an indicator and a {@link VolumeLevelTooltip}
|
||||
* indicating the volume level which is represented by a given point in the
|
||||
* {@link VolumeBar}.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class MouseVolumeLevelDisplay extends Component {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The {@link 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.update = Fn.throttle(Fn.bind(this, this.update), Fn.UPDATE_REFRESH_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the DOM element for this class.
|
||||
*
|
||||
* @return {Element}
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
className: 'vjs-mouse-display'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enquires updates to its own DOM as well as the DOM of its
|
||||
* {@link VolumeLevelTooltip} child.
|
||||
*
|
||||
* @param {Object} rangeBarRect
|
||||
* The `ClientRect` for the {@link VolumeBar} element.
|
||||
*
|
||||
* @param {number} rangeBarPoint
|
||||
* A number from 0 to 1, representing a horizontal/vertical reference point
|
||||
* from the left edge of the {@link VolumeBar}
|
||||
*
|
||||
* @param {boolean} vertical
|
||||
* Referees to the Volume control position
|
||||
* in the control bar{@link VolumeControl}
|
||||
*
|
||||
*/
|
||||
update(rangeBarRect, rangeBarPoint, vertical) {
|
||||
const volume = 100 * rangeBarPoint;
|
||||
|
||||
this.getChild('volumeLevelTooltip').updateVolume(rangeBarRect, rangeBarPoint, vertical, volume, () => {
|
||||
if (vertical) {
|
||||
this.el_.style.bottom = `${rangeBarRect.height * rangeBarPoint}px`;
|
||||
} else {
|
||||
this.el_.style.left = `${rangeBarRect.width * rangeBarPoint}px`;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default options for `MouseVolumeLevelDisplay`
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
MouseVolumeLevelDisplay.prototype.options_ = {
|
||||
children: [
|
||||
'volumeLevelTooltip'
|
||||
]
|
||||
};
|
||||
|
||||
Component.registerComponent('MouseVolumeLevelDisplay', MouseVolumeLevelDisplay);
|
||||
export default MouseVolumeLevelDisplay;
|
@ -4,9 +4,12 @@
|
||||
import Slider from '../../slider/slider.js';
|
||||
import Component from '../../component.js';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
import clamp from '../../utils/clamp.js';
|
||||
import {IS_IOS, IS_ANDROID} from '../../utils/browser.js';
|
||||
|
||||
// Required children
|
||||
import './volume-level.js';
|
||||
import './mouse-volume-level-display.js';
|
||||
|
||||
/**
|
||||
* The bar that contains the volume level and can be clicked on to adjust the level
|
||||
@ -71,6 +74,23 @@ class VolumeBar extends Slider {
|
||||
* @listens mousemove
|
||||
*/
|
||||
handleMouseMove(event) {
|
||||
const mouseVolumeLevelDisplay = this.getChild('mouseVolumeLevelDisplay');
|
||||
|
||||
if (mouseVolumeLevelDisplay) {
|
||||
const volumeBarEl = this.el();
|
||||
const volumeBarRect = Dom.getBoundingClientRect(volumeBarEl);
|
||||
const vertical = this.vertical();
|
||||
let volumeBarPoint = Dom.getPointerPosition(volumeBarEl, event);
|
||||
|
||||
volumeBarPoint = vertical ? volumeBarPoint.y : volumeBarPoint.x;
|
||||
// The default skin has a gap on either side of the `VolumeBar`. This means
|
||||
// that it's possible to trigger this behavior outside the boundaries of
|
||||
// the `VolumeBar`. This ensures we stay within it at all times.
|
||||
volumeBarPoint = clamp(volumeBarPoint, 0, 1);
|
||||
|
||||
mouseVolumeLevelDisplay.update(volumeBarRect, volumeBarPoint, vertical);
|
||||
}
|
||||
|
||||
if (!Dom.isSingleLeftClick(event)) {
|
||||
return;
|
||||
}
|
||||
@ -174,6 +194,11 @@ VolumeBar.prototype.options_ = {
|
||||
barName: 'volumeLevel'
|
||||
};
|
||||
|
||||
// MouseVolumeLevelDisplay tooltip should not be added to a player on mobile devices
|
||||
if (!IS_IOS && !IS_ANDROID) {
|
||||
VolumeBar.prototype.options_.children.splice(0, 0, 'mouseVolumeLevelDisplay');
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the update event for this Slider when this event happens on the player.
|
||||
*
|
||||
|
@ -44,6 +44,7 @@ class VolumeControl extends Component {
|
||||
|
||||
this.on('mousedown', this.handleMouseDown);
|
||||
this.on('touchstart', this.handleMouseDown);
|
||||
this.on('mousemove', this.handleMouseMove);
|
||||
|
||||
// while the slider is active (the mouse has been pressed down and
|
||||
// is dragging) or in focus we do not want to hide the VolumeBar
|
||||
|
133
src/js/control-bar/volume-control/volume-level-tooltip.js
Normal file
133
src/js/control-bar/volume-control/volume-level-tooltip.js
Normal file
@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @file volume-level-tooltip.js
|
||||
*/
|
||||
import Component from '../../component';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
|
||||
/**
|
||||
* Volume level tooltips display a volume above or side by side the volume bar.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class VolumeLevelTooltip extends Component {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The {@link 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.update = Fn.throttle(Fn.bind(this, this.update), Fn.UPDATE_REFRESH_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the volume tooltip DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
className: 'vjs-volume-tooltip'
|
||||
}, {
|
||||
'aria-hidden': 'true'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the position of the tooltip relative to the `VolumeBar` and
|
||||
* its content text.
|
||||
*
|
||||
* @param {Object} rangeBarRect
|
||||
* The `ClientRect` for the {@link VolumeBar} element.
|
||||
*
|
||||
* @param {number} rangeBarPoint
|
||||
* A number from 0 to 1, representing a horizontal/vertical reference point
|
||||
* from the left edge of the {@link VolumeBar}
|
||||
*
|
||||
* @param {boolean} vertical
|
||||
* Referees to the Volume control position
|
||||
* in the control bar{@link VolumeControl}
|
||||
*
|
||||
*/
|
||||
update(rangeBarRect, rangeBarPoint, vertical, content) {
|
||||
if (!vertical) {
|
||||
const tooltipRect = Dom.getBoundingClientRect(this.el_);
|
||||
const playerRect = Dom.getBoundingClientRect(this.player_.el());
|
||||
const volumeBarPointPx = rangeBarRect.width * rangeBarPoint;
|
||||
|
||||
if (!playerRect || !tooltipRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
const spaceLeftOfPoint = (rangeBarRect.left - playerRect.left) + volumeBarPointPx;
|
||||
const spaceRightOfPoint = (rangeBarRect.width - volumeBarPointPx) +
|
||||
(playerRect.right - rangeBarRect.right);
|
||||
let pullTooltipBy = tooltipRect.width / 2;
|
||||
|
||||
if (spaceLeftOfPoint < pullTooltipBy) {
|
||||
pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
|
||||
} else if (spaceRightOfPoint < pullTooltipBy) {
|
||||
pullTooltipBy = spaceRightOfPoint;
|
||||
}
|
||||
|
||||
if (pullTooltipBy < 0) {
|
||||
pullTooltipBy = 0;
|
||||
} else if (pullTooltipBy > tooltipRect.width) {
|
||||
pullTooltipBy = tooltipRect.width;
|
||||
}
|
||||
|
||||
this.el_.style.right = `-${pullTooltipBy}px`;
|
||||
}
|
||||
this.write(`${content}%`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the volume to the tooltip DOM element.
|
||||
*
|
||||
* @param {string} content
|
||||
* The formatted volume for the tooltip.
|
||||
*/
|
||||
write(content) {
|
||||
Dom.textContent(this.el_, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the position of the volume tooltip relative to the `VolumeBar`.
|
||||
*
|
||||
* @param {Object} rangeBarRect
|
||||
* The `ClientRect` for the {@link VolumeBar} element.
|
||||
*
|
||||
* @param {number} rangeBarPoint
|
||||
* A number from 0 to 1, representing a horizontal/vertical reference point
|
||||
* from the left edge of the {@link VolumeBar}
|
||||
*
|
||||
* @param {boolean} vertical
|
||||
* Referees to the Volume control position
|
||||
* in the control bar{@link VolumeControl}
|
||||
*
|
||||
* @param {number} volume
|
||||
* The volume level to update the tooltip to
|
||||
*
|
||||
* @param {Function} cb
|
||||
* A function that will be called during the request animation frame
|
||||
* for tooltips that need to do additional animations from the default
|
||||
*/
|
||||
updateVolume(rangeBarRect, rangeBarPoint, vertical, volume, cb) {
|
||||
this.requestNamedAnimationFrame('VolumeLevelTooltip#updateVolume', () => {
|
||||
this.update(rangeBarRect, rangeBarPoint, vertical, volume.toFixed(0));
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Component.registerComponent('VolumeLevelTooltip', VolumeLevelTooltip);
|
||||
export default VolumeLevelTooltip;
|
Loading…
Reference in New Issue
Block a user