aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSam Potts <sam@potts.es>2018-05-19 11:27:19 +1000
committerGitHub <noreply@github.com>2018-05-19 11:27:19 +1000
commit3bba65f2c22fe11bca7d89f8451fa1b0b5e8030e (patch)
treeab3de56cae1205b9964ecccb9dea57aa41a3695c /src
parent1bab0d07b5b22230aab6e68105c8fc574add31e3 (diff)
parent37c5fbfe16ba0969b727b8359fdd04eb0bf7a021 (diff)
downloadplyr-3bba65f2c22fe11bca7d89f8451fa1b0b5e8030e.tar.lz
plyr-3bba65f2c22fe11bca7d89f8451fa1b0b5e8030e.tar.xz
plyr-3bba65f2c22fe11bca7d89f8451fa1b0b5e8030e.zip
Merge pull request #967 from friday/883
toggleControls rewrite
Diffstat (limited to 'src')
-rw-r--r--src/js/defaults.js1
-rw-r--r--src/js/listeners.js95
-rw-r--r--src/js/plyr.js123
-rw-r--r--src/js/ui.js33
-rw-r--r--src/js/utils.js14
-rw-r--r--src/sass/plyr.scss1
-rw-r--r--src/sass/states/error.scss25
7 files changed, 103 insertions, 189 deletions
diff --git a/src/js/defaults.js b/src/js/defaults.js
index f160b1aa..da089efc 100644
--- a/src/js/defaults.js
+++ b/src/js/defaults.js
@@ -338,7 +338,6 @@ const defaults = {
paused: 'plyr--paused',
stopped: 'plyr--stopped',
loading: 'plyr--loading',
- error: 'plyr--has-error',
hover: 'plyr--hover',
tooltip: 'plyr__tooltip',
cues: 'plyr__cues',
diff --git a/src/js/listeners.js b/src/js/listeners.js
index 167bee3c..5dd9d93f 100644
--- a/src/js/listeners.js
+++ b/src/js/listeners.js
@@ -238,13 +238,36 @@ class Listeners {
}, 0);
});
- // Toggle controls visibility based on mouse movement
- if (this.player.config.hideControls) {
- // Toggle controls on mouse events and entering fullscreen
- utils.on(this.player.elements.container, 'mouseenter mouseleave mousemove touchstart touchend touchmove enterfullscreen exitfullscreen', event => {
- this.player.toggleControls(event);
- });
- }
+ // Toggle controls on mouse events and entering fullscreen
+ utils.on(this.player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', event => {
+ const { controls } = this.player.elements;
+
+ // Remove button states for fullscreen
+ if (event.type === 'enterfullscreen') {
+ controls.pressed = false;
+ controls.hover = false;
+ }
+
+ // Show, then hide after a timeout unless another control event occurs
+ const show = [
+ 'touchstart',
+ 'touchmove',
+ 'mousemove',
+ ].includes(event.type);
+
+ let delay = 0;
+
+ if (show) {
+ ui.toggleControls.call(this.player, true);
+ // Use longer timeout for touch devices
+ delay = this.player.touch ? 3000 : 2000;
+ }
+
+ // Clear timer
+ clearTimeout(this.player.timers.controls);
+ // Timer to prevent flicker when seeking
+ this.player.timers.controls = setTimeout(() => ui.toggleControls.call(this.player, false), delay);
+ });
}
// Listen for media events
@@ -283,9 +306,6 @@ class Listeners {
// Loading state
utils.on(this.player.media, 'waiting canplay seeked playing', event => ui.checkLoading.call(this.player, event));
- // Check if media failed to load
- // utils.on(this.player.media, 'play', event => ui.checkFailed.call(this.player, event));
-
// If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
utils.on(this.player.media, 'playing', () => {
@@ -574,26 +594,45 @@ class Listeners {
// Seek tooltip
on(this.player.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this.player, event));
- // Toggle controls visibility based on mouse movement
- if (this.player.config.hideControls) {
- // Watch for cursor over controls so they don't hide when trying to interact
- on(this.player.elements.controls, 'mouseenter mouseleave', event => {
- this.player.elements.controls.hover = !this.player.touch && event.type === 'mouseenter';
- });
+ // Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting)
+ on(this.player.elements.controls, 'mouseenter mouseleave', event => {
+ this.player.elements.controls.hover = !this.player.touch && event.type === 'mouseenter';
+ });
- // Watch for cursor over controls so they don't hide when trying to interact
- on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {
- this.player.elements.controls.pressed = [
- 'mousedown',
- 'touchstart',
- ].includes(event.type);
- });
+ // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
+ on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {
+ this.player.elements.controls.pressed = [
+ 'mousedown',
+ 'touchstart',
+ ].includes(event.type);
+ });
- // Focus in/out on controls
- on(this.player.elements.controls, 'focusin focusout', event => {
- this.player.toggleControls(event);
- });
- }
+ // Focus in/out on controls
+ on(this.player.elements.controls, 'focusin focusout', event => {
+ const { config, elements, timers } = this.player;
+
+ // Skip transition to prevent focus from scrolling the parent element
+ utils.toggleClass(elements.controls, config.classNames.noTransition, event.type === 'focusin');
+
+ // Toggle
+ ui.toggleControls.call(this.player, event.type === 'focusin');
+
+ // If focusin, hide again after delay
+ if (event.type === 'focusin') {
+ // Restore transition
+ setTimeout(() => {
+ utils.toggleClass(elements.controls, config.classNames.noTransition, false);
+ }, 0);
+
+ // Delay a little more for keyboard users
+ const delay = this.touch ? 3000 : 4000;
+
+ // Clear timer
+ clearTimeout(timers.controls);
+ // Hide
+ timers.controls = setTimeout(() => ui.toggleControls.call(this.player, false), delay);
+ }
+ });
// Mouse wheel for volume
on(
diff --git a/src/js/plyr.js b/src/js/plyr.js
index bed09827..ffd2a1e3 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -971,119 +971,32 @@ class Plyr {
/**
* Toggle the player controls
- * @param {boolean} toggle - Whether to show the controls
+ * @param {boolean} [toggle] - Whether to show the controls
*/
toggleControls(toggle) {
- // We need controls of course...
- if (!utils.is.element(this.elements.controls)) {
- return;
- }
-
- // Don't hide if no UI support or it's audio
- if (!this.supported.ui || this.isAudio) {
- return;
- }
-
- let delay = 0;
- let show = toggle;
- let isEnterFullscreen = false;
-
- // Get toggle state if not set
- if (!utils.is.boolean(toggle)) {
- if (utils.is.event(toggle)) {
- // Is the enter fullscreen event
- isEnterFullscreen = toggle.type === 'enterfullscreen';
-
- // Events that show the controls
- const showEvents = [
- 'touchstart',
- 'touchmove',
- 'mouseenter',
- 'mousemove',
- 'focusin',
- ];
-
- // Events that delay hiding
- const delayEvents = [
- 'touchmove',
- 'touchend',
- 'mousemove',
- ];
-
- // Whether to show controls
- show = showEvents.includes(toggle.type);
-
- // Delay hiding on move events
- if (delayEvents.includes(toggle.type)) {
- delay = 2000;
- }
-
- // Delay a little more for keyboard users
- if (!this.touch && toggle.type === 'focusin') {
- delay = 3000;
- utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, true);
- }
- } else {
- show = utils.hasClass(this.elements.container, this.config.classNames.hideControls);
- }
- }
+ // Don't toggle if missing UI support or if it's audio
+ if (this.supported.ui && !this.isAudio) {
+ // Get state before change
+ const isHidden = utils.hasClass(this.elements.container, this.config.classNames.hideControls);
- // Clear timer on every call
- clearTimeout(this.timers.controls);
+ // Negate the argument if not undefined since adding the class to hides the controls
+ const force = typeof toggle === 'undefined' ? undefined : !toggle;
- // If the mouse is not over the controls, set a timeout to hide them
- if (show || this.paused || this.loading) {
- // Check if controls toggled
- const toggled = utils.toggleClass(this.elements.container, this.config.classNames.hideControls, false);
-
- // Trigger event
- if (toggled) {
- utils.dispatchEvent.call(this, this.media, 'controlsshown');
- }
+ // Apply and get updated state
+ const hiding = utils.toggleClass(this.elements.container, this.config.classNames.hideControls, force);
- // Always show controls when paused or if touch
- if (this.paused || this.loading) {
- return;
+ // Close menu
+ if (hiding && this.config.controls.includes('settings') && !utils.is.empty(this.config.settings)) {
+ controls.toggleMenu.call(this, false);
}
-
- // Delay for hiding on touch
- if (this.touch) {
- delay = 3000;
+ // Trigger event on change
+ if (hiding !== isHidden) {
+ const eventName = hiding ? 'controlshidden' : 'controlsshown';
+ utils.dispatchEvent.call(this, this.media, eventName);
}
+ return !hiding;
}
-
- // If toggle is false or if we're playing (regardless of toggle),
- // then set the timer to hide the controls
- if (!show || this.playing) {
- this.timers.controls = setTimeout(() => {
- // We need controls of course...
- if (!utils.is.element(this.elements.controls)) {
- return;
- }
-
- // If the mouse is over the controls (and not entering fullscreen), bail
- if ((this.elements.controls.pressed || this.elements.controls.hover) && !isEnterFullscreen) {
- return;
- }
-
- // Restore transition behaviour
- if (!utils.hasClass(this.elements.container, this.config.classNames.hideControls)) {
- utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, false);
- }
-
- // Set hideControls class
- const toggled = utils.toggleClass(this.elements.container, this.config.classNames.hideControls, this.config.hideControls);
-
- // Trigger event and close menu
- if (toggled) {
- utils.dispatchEvent.call(this, this.media, 'controlshidden');
-
- if (this.config.controls.includes('settings') && !utils.is.empty(this.config.settings)) {
- controls.toggleMenu.call(this, false);
- }
- }
- }, delay);
- }
+ return false;
}
/**
diff --git a/src/js/ui.js b/src/js/ui.js
index 8f3f6a77..039144a5 100644
--- a/src/js/ui.js
+++ b/src/js/ui.js
@@ -173,7 +173,7 @@ const ui = {
}
// Toggle controls
- this.toggleControls(!this.playing);
+ ui.toggleControls.call(this);
},
// Check if media is loading
@@ -188,35 +188,22 @@ const ui = {
// Timer to prevent flicker when seeking
this.timers.loading = setTimeout(() => {
- // Toggle container class hook
+ // Update progress bar loading class state
utils.toggleClass(this.elements.container, this.config.classNames.loading, this.loading);
- // Show controls if loading, hide if done
- this.toggleControls(this.loading);
+ // Update controls visibility
+ ui.toggleControls.call(this);
}, this.loading ? 250 : 0);
},
- // Check if media failed to load
- checkFailed() {
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/networkState
- this.failed = this.media.networkState === 3;
+ // Toggle controls based on state and `force` argument
+ toggleControls(force) {
+ const { controls } = this.elements;
- if (this.failed) {
- utils.toggleClass(this.elements.container, this.config.classNames.loading, false);
- utils.toggleClass(this.elements.container, this.config.classNames.error, true);
+ if (controls && this.config.hideControls) {
+ // Show controls if force, loading, paused, or button interaction, otherwise hide
+ this.toggleControls(Boolean(force || this.loading || this.paused || controls.pressed || controls.hover));
}
-
- // Clear timer
- clearTimeout(this.timers.failed);
-
- // Timer to prevent flicker when seeking
- this.timers.loading = setTimeout(() => {
- // Toggle container class hook
- utils.toggleClass(this.elements.container, this.config.classNames.loading, this.loading);
-
- // Show controls if loading, hide if done
- this.toggleControls(this.loading);
- }, this.loading ? 250 : 0);
},
};
diff --git a/src/js/utils.js b/src/js/utils.js
index a58d8555..13e26655 100644
--- a/src/js/utils.js
+++ b/src/js/utils.js
@@ -393,14 +393,16 @@ const utils = {
}
},
- // Toggle class on an element
- toggleClass(element, className, toggle) {
+ // Mirror Element.classList.toggle, with IE compatibility for "force" argument
+ toggleClass(element, className, force) {
if (utils.is.element(element)) {
- const contains = element.classList.contains(className);
-
- element.classList[toggle ? 'add' : 'remove'](className);
+ let method = 'toggle';
+ if (typeof force !== 'undefined') {
+ method = force ? 'add' : 'remove';
+ }
- return (toggle && !contains) || (!toggle && contains);
+ element.classList[method](className);
+ return element.classList.contains(className);
}
return null;
diff --git a/src/sass/plyr.scss b/src/sass/plyr.scss
index 65134331..e934cf92 100644
--- a/src/sass/plyr.scss
+++ b/src/sass/plyr.scss
@@ -39,7 +39,6 @@
@import 'components/video';
@import 'components/volume';
-@import 'states/error';
@import 'states/fullscreen';
@import 'plugins/ads';
diff --git a/src/sass/states/error.scss b/src/sass/states/error.scss
deleted file mode 100644
index 64d05c7b..00000000
--- a/src/sass/states/error.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-// --------------------------------------------------------------
-// Error state
-// --------------------------------------------------------------
-
-.plyr--has-error {
- pointer-events: none;
-
- &::after {
- align-items: center;
- background: rgba(#000, 90%);
- color: #fff;
- content: attr(data-plyr-error);
- display: flex;
- font-size: $plyr-font-size-base;
- height: 100%;
- justify-content: center;
- left: 0;
- position: absolute;
- text-align: center;
- text-shadow: 0 1px 1px rgba(#000, 10%);
- top: 0;
- width: 100%;
- z-index: 10;
- }
-}