1
0
mirror of https://github.com/videojs/video.js.git synced 2025-10-31 00:08:01 +02:00

@gkatsev added an option to keep the tooltips inside the player bounds. closes #3149

This commit is contained in:
Gary Katsevman
2016-03-25 14:34:06 -04:00
parent bf95bb21cc
commit d4c1af60ac
8 changed files with 224 additions and 21 deletions

View File

@@ -7,6 +7,7 @@ CHANGELOG
* @kamilbrenk Added lang * @kamilbrenk Added lang
* @arius28 added greek translation file (el.json) ([view](https://github.com/videojs/video.js/pull/3185)) * @arius28 added greek translation file (el.json) ([view](https://github.com/videojs/video.js/pull/3185))
* @ricardosiri68 changed the relative sass paths ([view](https://github.com/videojs/video.js/pull/3147)) * @ricardosiri68 changed the relative sass paths ([view](https://github.com/videojs/video.js/pull/3147))
* @gkatsev added an option to keep the tooltips inside the player bounds ([view](https://github.com/videojs/video.js/pull/3149))
-------------------- --------------------

View File

@@ -500,6 +500,8 @@ module.exports = function(grunt) {
grunt.file.write(cssPath, css.replace(/(\\f\w+);/g, "'$1';")); grunt.file.write(cssPath, css.replace(/(\\f\w+);/g, "'$1';"));
}); });
grunt.registerTask('skin', ['sass', 'wrapcodepoints']);
// Default task - build and test // Default task - build and test
grunt.registerTask('default', ['test']); grunt.registerTask('default', ['test']);

View File

@@ -41,19 +41,46 @@ Player
BigPlayButton BigPlayButton
ControlBar ControlBar
PlayToggle PlayToggle
FullscreenToggle VolumeMenuButton
CurrentTimeDisplay CurrentTimeDisplay (Hidden by default)
TimeDivider TimeDivider (Hidden by default)
DurationDisplay DurationDisplay (Hidden by default)
RemainingTimeDisplay
ProgressControl ProgressControl
SeekBar SeekBar
LoadProgressBar LoadProgressBar
MouseTimeDisplay
PlayProgressBar PlayProgressBar
SeekHandle LiveDisplay (Hidden by default)
VolumeControl RemainingTimeDisplay
VolumeBar CustomControlsSpacer (No UI)
VolumeLevel ChaptersButton (Hidden by default)
VolumeHandle SubtitlesButton (Hidden by default)
MuteToggle CaptionsButton (Hidden by default)
FullscreenToggle
ErrorDisplay
TextTrackSettings
```
## Progress Control
The progress control is made up of the SeekBar. The seekbar contains the load progress bar
and the play progress bar. In addition, it contains the Mouse Time Display which
is used to display the time tooltip that follows the mouse cursor.
The play progress bar also has a time tooltip that show the current time.
By default, the progress control is sandwiched between the volume menu button and
the remaining time display inside the control bar, but in some cases, a skin would
want to move the progress control above the control bar and have it span the full
width of the player, in those cases, it is less than ideal to have the tooltips
get cut off or leave the bounds of the player. This can be prevented by setting the
`keepTooltipsInside` option on the progress control. This also makes the tooltips use
a real element instead of pseudo elements so targetting them with css will be different.
```js
let player = videojs('myplayer', {
controlBar: {
progressControl: {
keepTooltipsInside: true
}
}
});
``` ```

View File

@@ -39,15 +39,18 @@
to avoid a weird hitch when you roll off the hover. */ to avoid a weird hitch when you roll off the hover. */
// Also show the current time tooltip // Also show the current time tooltip
.video-js .vjs-progress-control:hover .vjs-time-tooltip,
.video-js .vjs-progress-control:hover .vjs-mouse-display:after, .video-js .vjs-progress-control:hover .vjs-mouse-display:after,
.video-js .vjs-progress-control:hover .vjs-play-progress:after { .video-js .vjs-progress-control:hover .vjs-play-progress:after {
display: block; font-family: VideoJS;
visibility: visible;
font-size: 0.6em; font-size: 0.6em;
} }
// Progress Bars // Progress Bars
.video-js .vjs-progress-holder .vjs-play-progress, .video-js .vjs-progress-holder .vjs-play-progress,
.video-js .vjs-progress-holder .vjs-load-progress, .video-js .vjs-progress-holder .vjs-load-progress,
.video-js .vjs-progress-holder .vjs-tooltip-progress-bar,
.video-js .vjs-progress-holder .vjs-load-progress div { .video-js .vjs-progress-holder .vjs-load-progress div {
position: absolute; position: absolute;
display: block; display: block;
@@ -83,12 +86,14 @@
// Current Time "tooltip" // Current Time "tooltip"
// By default this is hidden and only shown when hovering over the progress control // By default this is hidden and only shown when hovering over the progress control
.video-js .vjs-time-tooltip,
.video-js .vjs-mouse-display:after, .video-js .vjs-mouse-display:after,
.video-js .vjs-play-progress:after { .video-js .vjs-play-progress:after {
display: none; visibility: hidden;
pointer-events: none;
position: absolute; position: absolute;
top: -3.4em; top: -3.4em;
right: -1.5em; right: -1.9em;
font-size: 0.9em; font-size: 0.9em;
color: #000; color: #000;
content: attr(data-current-time); content: attr(data-current-time);
@@ -96,11 +101,17 @@
@include background-color-with-alpha(#fff, 0.8); @include background-color-with-alpha(#fff, 0.8);
@include border-radius(0.3em); @include border-radius(0.3em);
} }
.video-js .vjs-time-tooltip,
.video-js .vjs-play-progress:before, .video-js .vjs-play-progress:before,
.video-js .vjs-play-progress:after { .video-js .vjs-play-progress:after {
z-index: 1; z-index: 1;
} }
.video-js .vjs-progress-control .vjs-keep-tooltips-inside:after {
display: none;
}
.video-js .vjs-load-progress { .video-js .vjs-load-progress {
// For IE8 we'll lighten the color // For IE8 we'll lighten the color
background: lighten($secondary-background-color, 25%); background: lighten($secondary-background-color, 25%);
@@ -121,6 +132,18 @@
width: auto; width: auto;
} }
.video-js .vjs-time-tooltip {
display: inline-block;
height: 2.4em;
position: relative;
float: right;
right: -1.9em;
}
.vjs-tooltip-progress-bar {
visibility: hidden;
}
.video-js .vjs-progress-control .vjs-mouse-display { .video-js .vjs-progress-control .vjs-mouse-display {
display: none; display: none;
position: absolute; position: absolute;
@@ -146,6 +169,7 @@
.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display:after { .video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display:after {
display: none; display: none;
} }
.vjs-mouse-display .vjs-time-tooltip,
.video-js .vjs-progress-control .vjs-mouse-display:after { .video-js .vjs-progress-control .vjs-mouse-display:after {
color: #fff; color: #fff;
@include background-color-with-alpha(#000, 0.8); @include background-color-with-alpha(#000, 0.8);

View File

@@ -1,6 +1,7 @@
/** /**
* @file mouse-time-display.js * @file mouse-time-display.js
*/ */
import window from 'global/window';
import Component from '../../component.js'; import Component from '../../component.js';
import * as Dom from '../../utils/dom.js'; import * as Dom from '../../utils/dom.js';
import * as Fn from '../../utils/fn.js'; import * as Fn from '../../utils/fn.js';
@@ -21,6 +22,19 @@ class MouseTimeDisplay extends Component {
constructor(player, options) { constructor(player, options) {
super(player, options); super(player, options);
if (options.playerOptions &&
options.playerOptions.controlBar &&
options.playerOptions.controlBar.progressControl &&
options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
}
if (this.keepTooltipsInside) {
this.tooltip = Dom.createEl('div', {className: 'vjs-time-tooltip'});
this.el().appendChild(this.tooltip);
this.addClass('vjs-keep-tooltips-inside');
}
this.update(0, 0); this.update(0, 0);
player.on('ready', () => { player.on('ready', () => {
@@ -53,11 +67,50 @@ class MouseTimeDisplay extends Component {
this.el().style.left = position + 'px'; this.el().style.left = position + 'px';
this.el().setAttribute('data-current-time', time); this.el().setAttribute('data-current-time', time);
if (this.keepTooltipsInside) {
let clampedPosition = this.clampPosition_(position);
let difference = position - clampedPosition + 1;
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
let tooltipWidthHalf = tooltipWidth / 2;
this.tooltip.innerHTML = time;
this.tooltip.style.right = `-${tooltipWidthHalf - difference}px`;
}
} }
calculateDistance(event) { calculateDistance(event) {
return Dom.getPointerPosition(this.el().parentNode, event).x; return Dom.getPointerPosition(this.el().parentNode, event).x;
} }
/**
* This takes in a horizontal position for the bar and returns a clamped position.
* Clamped position means that it will keep the position greater than half the width
* of the tooltip and smaller than the player width minus half the width o the tooltip.
* It will only clamp the position if `keepTooltipsInside` option is set.
*
* @param {Number} position the position the bar wants to be
* @return {Number} newPosition the (potentially) clamped position
* @method clampPosition_
*/
clampPosition_(position) {
if (!this.keepTooltipsInside) {
return position;
}
let playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
let tooltipWidthHalf = tooltipWidth / 2;
let actualPosition = position;
if (position < tooltipWidthHalf) {
actualPosition = Math.ceil(tooltipWidthHalf);
} else if (position > (playerWidth - tooltipWidthHalf)) {
actualPosition = Math.floor(playerWidth - tooltipWidthHalf);
}
return actualPosition;
}
} }
Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay); Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);

View File

@@ -3,6 +3,7 @@
*/ */
import Component from '../../component.js'; import Component from '../../component.js';
import * as Fn from '../../utils/fn.js'; import * as Fn from '../../utils/fn.js';
import * as Dom from '../../utils/dom.js';
import formatTime from '../../utils/format-time.js'; import formatTime from '../../utils/format-time.js';
/** /**
@@ -20,6 +21,17 @@ class PlayProgressBar extends Component {
this.updateDataAttr(); this.updateDataAttr();
this.on(player, 'timeupdate', this.updateDataAttr); this.on(player, 'timeupdate', this.updateDataAttr);
player.ready(Fn.bind(this, this.updateDataAttr)); player.ready(Fn.bind(this, this.updateDataAttr));
if (options.playerOptions &&
options.playerOptions.controlBar &&
options.playerOptions.controlBar.progressControl &&
options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
}
if (this.keepTooltipsInside) {
this.addClass('vjs-keep-tooltips-inside');
}
} }
/** /**

View File

@@ -1,10 +1,12 @@
/** /**
* @file seek-bar.js * @file seek-bar.js
*/ */
import window from 'global/window';
import Slider from '../../slider/slider.js'; import Slider from '../../slider/slider.js';
import Component from '../../component.js'; import Component from '../../component.js';
import LoadProgressBar from './load-progress-bar.js'; import LoadProgressBar from './load-progress-bar.js';
import PlayProgressBar from './play-progress-bar.js'; import PlayProgressBar from './play-progress-bar.js';
import TooltipProgressBar from './tooltip-progress-bar.js';
import * as Fn from '../../utils/fn.js'; import * as Fn from '../../utils/fn.js';
import formatTime from '../../utils/format-time.js'; import formatTime from '../../utils/format-time.js';
import assign from 'object.assign'; import assign from 'object.assign';
@@ -21,8 +23,20 @@ class SeekBar extends Slider {
constructor(player, options){ constructor(player, options){
super(player, options); super(player, options);
this.on(player, 'timeupdate', this.updateARIAAttributes); this.on(player, 'timeupdate', this.updateProgress);
player.ready(Fn.bind(this, this.updateARIAAttributes)); this.on(player, 'ended', this.updateProgress);
player.ready(Fn.bind(this, this.updateProgress));
if (options.playerOptions &&
options.playerOptions.controlBar &&
options.playerOptions.controlBar.progressControl &&
options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
}
if (this.keepTooltipsInside) {
this.tooltipProgressBar = this.addChild('TooltipProgressBar');
}
} }
/** /**
@@ -44,11 +58,27 @@ class SeekBar extends Slider {
* *
* @method updateARIAAttributes * @method updateARIAAttributes
*/ */
updateARIAAttributes() { updateProgress() {
// Allows for smooth scrubbing, when player can't keep up. this.updateAriaAttributes(this.el_);
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
this.el_.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2)); // machine readable value of progress bar (percentage complete) if (this.keepTooltipsInside) {
this.el_.setAttribute('aria-valuetext', formatTime(time, this.player_.duration())); // human readable value of progress bar (time complete) this.updateAriaAttributes(this.tooltipProgressBar.el_);
this.tooltipProgressBar.el_.style.width = this.bar.el_.style.width;
let playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltipProgressBar.tooltip).width);
let tooltipStyle = this.tooltipProgressBar.el().style;
tooltipStyle.maxWidth = Math.floor(playerWidth - (tooltipWidth / 2)) + 'px';
tooltipStyle.minWidth = Math.ceil(tooltipWidth / 2) + 'px';
tooltipStyle.right = `-${tooltipWidth / 2}px`;
}
}
updateAriaAttributes(el) {
// Allows for smooth scrubbing, when player can't keep up.
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2)); // machine readable value of progress bar (percentage complete)
el.setAttribute('aria-valuetext', formatTime(time, this.player_.duration())); // human readable value of progress bar (time complete)
} }
/** /**

View File

@@ -0,0 +1,54 @@
/**
* @file play-progress-bar.js
*/
import Component from '../../component.js';
import * as Fn from '../../utils/fn.js';
import * as Dom from '../../utils/dom.js';
import formatTime from '../../utils/format-time.js';
/**
* Shows play progress
*
* @param {Player|Object} player
* @param {Object=} options
* @extends Component
* @class PlayProgressBar
*/
class TooltipProgressBar extends Component {
constructor(player, options){
super(player, options);
this.updateDataAttr();
this.on(player, 'timeupdate', this.updateDataAttr);
player.ready(Fn.bind(this, this.updateDataAttr));
}
/**
* Create the component's DOM element
*
* @return {Element}
* @method createEl
*/
createEl() {
let el = super.createEl('div', {
className: 'vjs-tooltip-progress-bar vjs-slider-bar',
innerHTML: `<div class="vjs-time-tooltip"></div>
<span class="vjs-control-text"><span>${this.localize('Progress')}</span>: 0%</span>`
});
this.tooltip = el.querySelector('.vjs-time-tooltip');
return el;
}
updateDataAttr() {
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
let formattedTime = formatTime(time, this.player_.duration());
this.el_.setAttribute('data-current-time', formattedTime);
this.tooltip.innerHTML = formattedTime;
}
}
Component.registerComponent('TooltipProgressBar', TooltipProgressBar);
export default TooltipProgressBar;