From 6438fb816097f65d17865540fe6e5e67d3064b99 Mon Sep 17 00:00:00 2001 From: Andy Niccolai Date: Fri, 28 Jun 2013 12:22:05 -0600 Subject: [PATCH 1/8] Prevent control bar from attatching event listeners when disabled. Fixes #556 --- src/js/control-bar/control-bar.js | 78 +++++++++++++++++-------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/js/control-bar/control-bar.js b/src/js/control-bar/control-bar.js index 20510cd832..9e7e12fb86 100644 --- a/src/js/control-bar/control-bar.js +++ b/src/js/control-bar/control-bar.js @@ -9,50 +9,60 @@ vjs.ControlBar = vjs.Component.extend({ init: function(player, options){ vjs.Component.call(this, player, options); - if (!player.controls()) { + // Controls are always off + if (player.controls() === false) { this.disable(); + // Controls are always on + } else if (player.controls() === true) { + this.setup(); + // Controls fade when not in use + } else { + this.disable(); + this.setup(); } + } +}); - player.one('play', vjs.bind(this, function(){ - var touchstart, - fadeIn = vjs.bind(this, this.fadeIn), - fadeOut = vjs.bind(this, this.fadeOut); +vjs.ControlBar.prototype.setup = function(){ + this.player_.one('play', vjs.bind(this, function(){ + var touchstart, + fadeIn = vjs.bind(this, this.fadeIn), + fadeOut = vjs.bind(this, this.fadeOut); - this.fadeIn(); + this.fadeIn(); - if ( !('ontouchstart' in window) ) { - this.player_.on('mouseover', fadeIn); - this.player_.on('mouseout', fadeOut); - this.player_.on('pause', vjs.bind(this, this.lockShowing)); - this.player_.on('play', vjs.bind(this, this.unlockShowing)); - } + if ( !('ontouchstart' in window) ) { + this.player_.on('mouseover', fadeIn); + this.player_.on('mouseout', fadeOut); + this.player_.on('pause', vjs.bind(this, this.lockShowing)); + this.player_.on('play', vjs.bind(this, this.unlockShowing)); + } + touchstart = false; + this.player_.on('touchstart', function() { + touchstart = true; + }); + this.player_.on('touchmove', function() { touchstart = false; - this.player_.on('touchstart', function() { - touchstart = true; - }); - this.player_.on('touchmove', function() { - touchstart = false; - }); - this.player_.on('touchend', vjs.bind(this, function(event) { - var idx; - if (touchstart) { - idx = this.el().className.search('fade-in'); - if (idx !== -1) { - this.fadeOut(); - } else { - this.fadeIn(); - } + }); + this.player_.on('touchend', vjs.bind(this, function(event) { + var idx; + if (touchstart) { + idx = this.el().className.search('fade-in'); + if (idx !== -1) { + this.fadeOut(); + } else { + this.fadeIn(); } - touchstart = false; + } + touchstart = false; - if (!this.player_.paused()) { - event.preventDefault(); - } - })); + if (!this.player_.paused()) { + event.preventDefault(); + } })); - } -}); + })); +} vjs.ControlBar.prototype.options_ = { loadEvent: 'play', From a9d87b433fa3a4bbf402dc31d2a2d0eae97f6518 Mon Sep 17 00:00:00 2001 From: Steve Heffernan Date: Fri, 19 Jul 2013 20:19:50 -0700 Subject: [PATCH 2/8] Created user state events to aid in control bar hiding --- src/js/control-bar/control-bar.js | 6 +- src/js/player.js | 144 ++++++++++++++++++++++++++++++ test/unit/control-bar.js | 0 3 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 test/unit/control-bar.js diff --git a/src/js/control-bar/control-bar.js b/src/js/control-bar/control-bar.js index 9e7e12fb86..c55f2db408 100644 --- a/src/js/control-bar/control-bar.js +++ b/src/js/control-bar/control-bar.js @@ -26,8 +26,8 @@ vjs.ControlBar = vjs.Component.extend({ vjs.ControlBar.prototype.setup = function(){ this.player_.one('play', vjs.bind(this, function(){ var touchstart, - fadeIn = vjs.bind(this, this.fadeIn), - fadeOut = vjs.bind(this, this.fadeOut); + fadeIn = vjs.bind(this, this.fadeIn), + fadeOut = vjs.bind(this, this.fadeOut); this.fadeIn(); @@ -94,4 +94,4 @@ vjs.ControlBar.prototype.fadeIn = function(){ vjs.ControlBar.prototype.fadeOut = function(){ vjs.Component.prototype.fadeOut.call(this); this.player_.trigger('controlshidden'); -}; \ No newline at end of file +}; diff --git a/src/js/player.js b/src/js/player.js index 57ae3448a8..358212da2d 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -71,6 +71,8 @@ vjs.Player = vjs.Component.extend({ this[key](val); }, this); } + + this.listenForUserActivity(); } }); @@ -861,6 +863,146 @@ vjs.Player.prototype.controls = function(controls){ vjs.Player.prototype.error = function(){ return this.techGet('error'); }; vjs.Player.prototype.ended = function(){ return this.techGet('ended'); }; +vjs.Player.prototype.userActive_ = false; +vjs.Player.prototype.userActive = function(bool){ + if (bool !== undefined) { + bool = !!bool; + if (bool !== this.userActive_) { + this.userActive_ = bool; + if (bool) { + this.removeClass('vjs-user-passive'); + this.addClass('vjs-user-active'); + this.trigger('useractive'); + console.log('useractive'); + } else { + this.removeClass('vjs-user-active'); + this.addClass('vjs-user-passive'); + this.trigger('userpassive'); + console.log('userpassive'); + } + } + return this; + } + return this.userActive_; +}; + +vjs.Player.prototype.listenForUserActivity = function(){ + var activity, onActivity, activityCheck, inactivityTimeout, + touchstart, touchmove, touchtime, touchActiveInterval; + + // When the player is first initialized, trigger activity so components + // like the control bar show themselves + activity = true; + + // Anything considered deliberate user activity should set activity to true + onActivity = function(){ + activity = true; + }; + + // When not on touch devices, any mouse movement will be considered + // user activity + if (!('ontouchstart' in window)) { + this.on('mousedown', onActivity); + this.on('mousemove', onActivity); + } + + // On touch devices user activity gets a little more complicated. + // A tap can signal that a user has become active, or has become passive + // e.g. a quick tap on an iPhone movie should reveal the controls. Another + // quick tap should hide them again (signaling the user is in a passive + // viewing state) + // + // In addition to this, we still want the user to be considered passive after + // a few seconds of inactivity + touchstart = 0; + this.on('touchstart', function() { + // Record start time so we can detect a "touch and hold" vs. an immediate + // release. Touch and holds do not count as an active/passive toggle. + touchstart = new Date().getTime(); + + // If a user is already active, we want to continue to consider them active. + // If they're passive, we want to wait and see if this is a fast tap + // before considering them active again. (same as iOS behavior) + if (this.userActive_) { + activity = true; + // For as long as the they are touching the device, we consider them acitve + // even if they're not moving their finger. So we want to continue ot update + // that they are active + clearInterval(touchActiveInterval); + touchActiveInterval = setInterval(function(){ + // Check for touchstart to make sure this insn't called after touchend + if (touchstart > 0) { + activity = true; + } + // Setting activity=true now and setting the interval to the same time + // as the activityCheck interval should ensure we never miss the + // next activityCheck + }, 250); + } + }); + + // When the touch ends, determine how we should update the user state + this.on('touchend', vjs.bind(this, function(event) { + // Measure how long the touch lasted + touchtime = new Date().getTime() - touchstart; + // Stop the interval that maintains activity if it's running + clearInterval(touchActiveInterval); + // Reset touchstart so we know a touch is no longer in process + touchstart = 0; + // If the user is already active we want this event to count towards their + // activity, no matter if this is a tap, touch and hold, or touch + move + if (this.userActive_) { + activity = true; + } + + // The touch needs to be quick in order to consider it a tap (?). Otherwise + // it's a touch and hold which should not toggle the user active state + if (touchtime < 250) { + if (this.userActive_) { + // We're switching the state to passive manually, so erase any other + // activity + activity = false; + this.userActive(false); + } else { + // If the user was inactive and is now active we want to reset the + // inactivity timer + activity = true; + this.userActive(true); + } + } + + // if (!this.paused()) { + // event.preventDefault(); + // } + })); + + // Run an interval every 250 milliseconds instead of stuffing everything into + // the mousemove function itself, to prevent performance degradation. + // http://ejohn.org/blog/learning-from-twitter/ + activityCheck = setInterval(vjs.bind(this, function() { + // Check to see if the mouse has been moved + if (activity) { + // Reset the activity tracker + activity = false; + + // If the user state was passive, set the state to active + if (!this.userActive_) { + this.userActive(true); + } + + // Clear any existing inactivity timeout to start the timer over + clearTimeout(inactivityTimeout); + + // In X seconds, if no more activity has occurred (resetting this timer) + // the user will be considered passive + inactivityTimeout = setTimeout(vjs.bind(this, function() { + this.userActive(false); + }), 2000); + console.log('inactivityTimeout') + } + }), 250); +}; + // Methods to add support for // networkState: function(){ return this.techCall('networkState'); }, // readyState: function(){ return this.techCall('readyState'); }, @@ -928,3 +1070,5 @@ vjs.Player.prototype.ended = function(){ return this.techGet('ended'); }; } })(); + + diff --git a/test/unit/control-bar.js b/test/unit/control-bar.js new file mode 100644 index 0000000000..e69de29bb2 From ccabf6079c7d35e6fd2954d9607e102d2cd66d96 Mon Sep 17 00:00:00 2001 From: Steve Heffernan Date: Mon, 29 Jul 2013 14:55:16 -0700 Subject: [PATCH 3/8] Control bar overhaul to fix multiple issues. #556, #500, #374, #281, #403, #561, #441, #193 --- src/css/video-js.less | 136 +++++++++++++------ src/js/big-play-button.js | 10 +- src/js/button.js | 3 +- src/js/component.js | 48 +++++++ src/js/control-bar/control-bar.js | 63 ++------- src/js/lib.js | 1 + src/js/media/flash.js | 2 +- src/js/media/html5.js | 14 +- src/js/media/media.js | 147 ++++++++++++++++---- src/js/player.js | 216 +++++++++++++++--------------- test/unit/component.js | 18 +++ test/unit/media.html5.js | 6 +- test/unit/player.js | 21 ++- 13 files changed, 447 insertions(+), 238 deletions(-) diff --git a/src/css/video-js.less b/src/css/video-js.less index d3cf525220..deac25116d 100644 --- a/src/css/video-js.less +++ b/src/css/video-js.less @@ -4,11 +4,11 @@ Version GENERATED_AT_BUILD Create your own skin at http://designer.videojs.com */ -// To customize the player skin, change the values of the variables or edit the +// To customize the player skin, change the values of the variables or edit the // CSS below. // (This file uses LESS. Learn more at http://lesscss.org/) -// The base font size controls the size of everything, not just text. All +// The base font size controls the size of everything, not just text. All // diminensions use em-based sizes so that the scale along with the font size. // Try increasing it to 20px and see what happens. @base-font-size: 10px; @@ -16,8 +16,8 @@ Create your own skin at http://designer.videojs.com // The main font color controls the color of the text and the icons (font icons) @main-font-color: #CCCCCC; // e.g. rgb(255, 255, 255) or #ffffff -// The default color of control backgrounds is mostly black but with a little -// bit of blue so it can still be seen on all black video frames, which are +// The default color of control backgrounds is mostly black but with a little +// bit of blue so it can still be seen on all black video frames, which are // common. @control-bg-color: #07141E; // e.g. rgb(255, 255, 255) or #ffffff @control-bg-alpha: 0.7; // 1.0 = 100% opacity, 0.0 = 0% opacity @@ -32,12 +32,12 @@ Create your own skin at http://designer.videojs.com @slider-background-color: #333333; @slider-background-alpha: 0.9; // 1.0 = 100% opacity, 0.0 = 0% opacity -// The "Big Play Button" is the play button that shows before the video plays. -// To center it set the align values to center and middle. The typical location +// The "Big Play Button" is the play button that shows before the video plays. +// To center it set the align values to center and middle. The typical location // of the button is the center, but there is trend towards moving it to a corner // where it gets out of the way of valuable content in the poster image. @big-play-align: left; // left, center, or right -@big-play-vertical-align: top; // top, middle, or bottom +@big-play-vertical-align: top; // top, middle, or bottom // The button colors match the control colors by default but you can customize // them by replace the variables (@control-bg-color) with your own color values. @big-play-bg-color: @control-bg-color; @@ -58,9 +58,9 @@ Create your own skin at http://designer.videojs.com /* SKIN ================================================================================ -The main class name for all skin-specific styles. To make your own skin, -replace all occurances of 'vjs-default-skin' with a new name. Then add your new -skin name to your video tag instead of the default skin. +The main class name for all skin-specific styles. To make your own skin, +replace all occurances of 'vjs-default-skin' with a new name. Then add your new +skin name to your video tag instead of the default skin. e.g.