From 2c41c8707497a1a61edd00796c9df97ead5dac89 Mon Sep 17 00:00:00 2001 From: josex2r Date: Tue, 21 Jul 2015 19:51:59 +0200 Subject: [PATCH] Fixed '_getInstance' for multiple selectors Example (stop machines at the same time): ```javascript $('#machine1, #machine2, #machine3') .slotMachine() .forEach(function(instance){ instance.stop(); }); ``` --- dist/jquery.slotmachine.js | 1120 ++++++++++++++++---------------- dist/jquery.slotmachine.min.js | 2 +- src/jquery.slotmachine.js | 8 +- 3 files changed, 564 insertions(+), 566 deletions(-) diff --git a/dist/jquery.slotmachine.js b/dist/jquery.slotmachine.js index 46fe2f5..8ff567e 100644 --- a/dist/jquery.slotmachine.js +++ b/dist/jquery.slotmachine.js @@ -1,565 +1,563 @@ /*! SlotMachine - v2.0.11 - 2015-07-21 * https://github.com/josex2r/jQuery-SlotMachine * Copyright (c) 2015 Jose Luis Represa; Licensed MIT */ -;(function($, window, document, undefined){ - - var pluginName = "slotMachine", - defaults = { - active : 0, //Active element [int] - delay : 200, //Animation time [int] - auto : false, //Repeat delay [false||int] - randomize : null, //Randomize function, must return an integer with the selected position - complete : null, //Callback function(result) - stopHidden : true //Stops animations if the element isn´t visible on the screen - }; - - var FX_FAST = 'slotMachineBlurFast', - FX_NORMAL = 'slotMachineBlurMedium', - FX_SLOW = 'slotMachineBlurSlow', - FX_GRADIENT = 'slotMachineGradient', - FX_STOP = FX_GRADIENT; - - //Set required styles, filters and masks - $(document).ready(function(){ - - var slotMachineBlurFilterFastString = ''+ - ''+ - ''+ - ''+ - '#slotMachineBlurFilterFast'; - - var slotMachineBlurFilterMediumString = ''+ - ''+ - ''+ - ''+ - '#slotMachineBlurFilterMedium'; - - var slotMachineBlurFilterSlowString = ''+ - ''+ - ''+ - ''+ - '#slotMachineBlurFilterSlow'; - - var slotMachineFadeMaskString = ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - '#slotMachineFadeMask'; - - //CSS classes - $('body').append(''); - - }); - - //Required easing functions - if(typeof $.easing.easeOutBounce !== 'function'){ - //From jQuery easing, extend jQuery animations functions - $.extend( $.easing, { - easeOutBounce: function (x, t, b, c, d) { - if ((t/=d) < (1/2.75)) { - return c*(7.5625*t*t) + b; - } else if (t < (2/2.75)) { - return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b; - } else if (t < (2.5/2.75)) { - return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b; - } else { - return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b; - } - }, - }); - } - - - function Timer(fn, delay){ - var startTime, - self = this, - timer, - _fn = fn, - _args = arguments, - _delay = delay; - - this.running = false; - - this.onpause = function(){}; - this.onresume = function(){}; - - this.cancel = function(){ - this.running = false; - clearTimeout(timer); - }; - - this.pause = function(){ - if( this.running ){ - delay -= new Date().getTime() - startTime; - this.cancel(); - this.onpause(); - } - }; - - this.resume = function(){ - if( !this.running ){ - this.running = true; - startTime = new Date().getTime(); - - timer = setTimeout(function(){ - _fn.apply(self, Array.prototype.slice.call(_args, 2, _args.length)); //Execute function with initial arguments, removing (fn & delay) - }, delay); - - this.onresume(); - } - }; - - this.reset = function(){ - this.cancel(); - this.running = true; - delay = _delay; - timer = setTimeout(function(){ - _fn.apply(self, Array.prototype.slice.call(_args, 2, _args.length)); //Execute function with initial arguments, removing (fn & delay) - }, _delay); - }; - - this.add = function(extraDelay){ - this.pause(); - delay += extraDelay; - this.resume(); +(function($, window, document, undefined) { + + var pluginName = "slotMachine", + defaults = { + active: 0, //Active element [int] + delay: 200, //Animation time [int] + auto: false, //Repeat delay [false||int] + spins: 5, //Number of spins when auto [int] + randomize: null, //Randomize function, must return an integer with the selected position + complete: null, //Callback function(result) + stopHidden: true //Stops animations if the element isn´t visible on the screen }; - - this.resume(); - } - - - /** - * @desc PUBLIC - Makes Slot Machine animation effect - * @param DOM element - Html element - * @param object settings - Plugin configuration params - * @return jQuery node - Returns jQuery selector with some new functions (shuffle, stop, next, auto, active) - */ - function SlotMachine(element, options){ - this.element = element; - this.settings = $.extend( {}, defaults, options); - this.defaults = defaults; - this.name = pluginName; - - //jQuery selector - this.$slot = $(element); - //Slot Machine elements - this.$tiles = this.$slot.children(); - //Container to wrap $tiles - this.$container = null; - //Min marginTop offset - this._minTop = null; - //Max marginTop offset - this._maxTop = null; - //First element (the last of the html container) - this._$fakeFirstTile = null; - //Last element (the first of the html container) - this._$fakeLastTile = null; - //Timeout recursive function to handle auto (settings.auto) - this._timer = null; - //Callback function - this._oncompleteStack = [ this.settings.complete ]; - //Number of spins left before stop - this._spinsLeft = null; - //Future result - this.futureActive = null; - //Machine is running? - this.isRunning = false; - //Machine is stopping? - this.isStopping = false; - //Current active element - this.active = this.settings.active; - - this.$slot.css("overflow", "hidden"); - - //Validate active index - if(this.settings.active < 0 || this.settings.active >= this.$tiles.length ){ - this.settings.active = 0; - this.active = 0; - } - - //Wrap elements inside $container - this.$tiles.wrapAll("
"); - this.$container = this.$slot.find(".slotMachineContainer"); - - //Set max top offset - this._maxTop = - this.$container.height(); - - //Add the last element behind the first to prevent the jump effect - this._$fakeFirstTile = this.$tiles.last().clone(); - this._$fakeLastTile = this.$tiles.first().clone(); - - this.$container.prepend( this._$fakeFirstTile ); - this.$container.append( this._$fakeLastTile ); - - //Set min top offset - this._minTop = - this._$fakeFirstTile.outerHeight(); - - //Show active element - this.$container.css('margin-top', this.getTileOffset(this.active)); - - //Start auto animation - if(this.settings.auto !== false){ - if(this.settings.auto === true){ - this.shuffle(); - }else{ - this.auto(); - } - } - } - /** - * @desc PRIVATE - Get element offset top - * @param int index - Element position - * @return int - Negative offset in px - */ - SlotMachine.prototype.getTileOffset = function(index){ - var offset = 0; - for(var i=0; i= 0); - - return rnd; - }; - /** - * @desc PRIVATE - Get random element based on the custom randomize function - * @return int - Element index - */ - SlotMachine.prototype.getCustom = function(){ - var choosen; - if(this.settings.randomize !== null && typeof this.settings.randomize === 'function'){ - var index = this.settings.randomize.apply(this, [this.active]); - if(index < 0 || index >= this.$tiles.length){ - index = 0; - } - choosen = index; - }else{ - choosen = this.getRandom(); - } - return choosen; - }; - /** - * @desc PRIVATE - Get the previous element - * @return int - Element index - */ - SlotMachine.prototype.getPrev = function(){ - var prevIndex = (this.active-1 < 0) ? (this.$tiles.length - 1) : (this.active - 1); - return prevIndex; - }; - /** - * @desc PRIVATE - Get the next element - * @return int - Element index - */ - SlotMachine.prototype.getNext = function(){ - var nextIndex = (this.active + 1 < this.$tiles.length) ? (this.active + 1) : 0; - return nextIndex; - }; - /** - * @desc PRIVATE - Set CSS classes to make speed effect - * @param string FX_SPEED - Element speed [FX_FAST_BLUR||FX_NORMAL_BLUR||FX_SLOW_BLUR||FX_STOP] - * @param string||boolean fade - Set fade gradient effect - */ - SlotMachine.prototype._setAnimationFX = function(FX_SPEED, fade){ - var self = this; - - setTimeout(function(){ - self.$tiles.removeClass(FX_FAST).removeClass(FX_NORMAL).removeClass(FX_SLOW).addClass(FX_SPEED); - - if(fade !== true || FX_SPEED === FX_STOP){ - self.$slot.add(self.$tiles).removeClass(FX_GRADIENT); - }else{ - self.$slot.add(self.$tiles).addClass(FX_GRADIENT); - } - }, this.settings.delay / 4); - }; - /** - * @desc PRIVATE - Reset active element position - */ - SlotMachine.prototype._resetPosition = function(){ - this.$container.css("margin-top", this.getTileOffset(this.active)); - }; - /** - * @desc PRIVATE - Checks if the machine is on the screen - * @return int - Returns true if machine is on the screen - */ - SlotMachine.prototype.isVisible = function(){ - //Stop animation if element is [above||below] screen, best for performance - var above = this.$slot.offset().top > $(window).scrollTop() + $(window).height(), - below = $(window).scrollTop() > this.$slot.height() + this.$slot.offset().top; - - return !above && !below; - }; - /** - * @desc PUBLIC - SELECT previous element relative to the current active element - * @return int - Returns result index - */ - SlotMachine.prototype.prev = function(){ - this.futureActive = this.getPrev(); - this.isRunning = true; - this.stop(false); - return this.futureActive; - }; - /** - * @desc PUBLIC - SELECT next element relative to the current active element - * @return int - Returns result index - */ - SlotMachine.prototype.next = function(){ - this.futureActive = this.getNext(); - this.isRunning = true; - this.stop(false); - return this.futureActive; - }; - /** - * @desc PRIVATE - Starts shuffling the elements - * @param int repeations - Number of shuffles (undefined to make infinite animation - * @return int - Returns result index - */ - SlotMachine.prototype.shuffle = function( spins, onComplete ){ - var self = this; - - if(onComplete !== undefined){ - //this._oncompleteStack.push(onComplete); - this._oncompleteStack[1] = onComplete; - } - - this.isRunning = true; - var delay = this.settings.delay; - - if(this.futureActive === null){ - //Get random or custom element - var rnd = this.getCustom(); - this.futureActive = rnd; - } - - /*if(this.$slot.attr("id")==="machine1") - console.log(this.futureActive)*/ - //Decreasing spin - if(typeof spins === 'number'){ - //Change delay and speed - switch( spins){ - case 1: - case 2: - this._setAnimationFX(FX_SLOW, true); - break; - case 3: - case 4: - this._setAnimationFX(FX_NORMAL, true); - delay /= 1.5; - break; - default: - this._setAnimationFX(FX_FAST, true); - delay /= 2; - } - //Infinite spin - }else{ - //Set animation effects - this._setAnimationFX(FX_FAST, true); - delay /= 2; - } - - //Perform animation - if(!this.isVisible() && this.settings.stopHidden === true){ - spins = 0; - self.stop(); - }else{ - this.$container.animate({ - marginTop : this._maxTop - }, delay, 'linear', function(){ - //Reset top position - self.$container.css('margin-top', 0); - - if(spins - 1 <= 0){ - self.stop(); - }else{ - //Repeat animation - self.shuffle(spins - 1); - } - }); - } - - return this.futureActive; - }; - /** - * @desc PRIVATE - Stop shuffling the elements - * @return int - Returns result index - */ - SlotMachine.prototype.stop = function( showGradient ){ - if(!this.isRunning){ - return; - } else if (this.isStopping) { - return this.futureActive; - } - var self = this; - - //Stop animation NOW!!!!!!! - this.$container.clearQueue().stop(true, false); - - this._setAnimationFX(FX_SLOW, showGradient === undefined ? true : showGradient); - - this.isRunning = true; - this.isStopping = true; - - //Set current active element - this.active = this.getVisibleTile(); - - //Check direction to prevent jumping - if(this.futureActive > this.active){ - //We are moving to the prev (first to last) - if(this.active === 0 && this.futureActive === this.$tiles.length-1){ - this.$container.css('margin-top', this.getTileOffset(this.$tiles.length) ); - } - }else{ - //We are moving to the next (last to first) - if(this.active === this.$tiles.length - 1 && this.futureActive === 0){ - this.$container.css('margin-top', 0); - } - } - - //Update last choosen element index - this.active = this.futureActive; - - //Get delay - var delay = this.settings.delay * 3; - - //Perform animation - this.$container.animate({ - marginTop : this.getTileOffset(this.active) - }, delay, 'easeOutBounce', function (){ - - self.isStopping = false; - self.isRunning = false; - self.futureActive = null; - - //Filter callbacks - /* - self._oncompleteStack = Array.prototype.filter.call(self._oncompleteStack, function(fn){ - return typeof fn === 'function'; - }); - //Ececute callbacks - Array.prototype.map.call(self._oncompleteStack, function(fn, index){ - if(typeof fn === 'function'){ - fn.apply(self, [self.active]); - self._oncompleteStack[index] = null; - } - }); - */ - if(typeof self._oncompleteStack[0] === 'function'){ - self._oncompleteStack[0].apply(self, [self.active]); - } - if(typeof self._oncompleteStack[1] === 'function'){ - self._oncompleteStack[1].apply(self, [self.active]); - } - }); - - //Disable blur - setTimeout(function(){ - self._setAnimationFX(FX_STOP, false); - }, delay / 1.75); - - return this.active; - }; - /** - * @desc PRIVATE - Start auto shufflings, animation stops each 3 repeations. Then restart animation recursively - */ - SlotMachine.prototype.auto = function(){ - var self = this; - - this._timer = new Timer(function(){ - if(typeof self.settings.randomize !== 'function'){ - self.futureActive = self.getNext(); - } - self.isRunning = true; - if(!self.isVisible() && self.settings.stopHidden === true){ - setTimeout(function(){ - self._timer.reset(); - }, 500); - }else{ - self.shuffle(5, function(){ - self._timer.reset(); - }); - } - - }, this.settings.auto); - }; - - - - /* - * Create new plugin instance if needed and return it - */ - function _getInstance(element, options){ - var machine; - if ( !$.data(element[0], 'plugin_' + pluginName) ){ - machine = new SlotMachine(element, options); - $.data(element[0], 'plugin_' + pluginName, machine); - }else{ - machine = $.data(element[0], 'plugin_' + pluginName); - } - return machine; - } - - /* - * Chainable instance - */ - $.fn[pluginName] = function(options){ - if( this.length===1 ){ - return _getInstance(this, options); - }else{ - return this.each(function(){ - if( !$.data(this, 'plugin_' + pluginName) ){ - _getInstance(this, options); - } - }); - } - }; - -})( jQuery, window, document ); + + var FX_FAST = 'slotMachineBlurFast', + FX_NORMAL = 'slotMachineBlurMedium', + FX_SLOW = 'slotMachineBlurSlow', + FX_GRADIENT = 'slotMachineGradient', + FX_STOP = FX_GRADIENT; + + //Set required styles, filters and masks + $(document).ready(function() { + + var slotMachineBlurFilterFastString = '' + + '' + + '' + + '' + + '#slotMachineBlurFilterFast'; + + var slotMachineBlurFilterMediumString = '' + + '' + + '' + + '' + + '#slotMachineBlurFilterMedium'; + + var slotMachineBlurFilterSlowString = '' + + '' + + '' + + '' + + '#slotMachineBlurFilterSlow'; + + var slotMachineFadeMaskString = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '#slotMachineFadeMask'; + + //CSS classes + $('body').append(''); + + }); + + //Required easing functions + if (typeof $.easing.easeOutBounce !== 'function') { + //From jQuery easing, extend jQuery animations functions + $.extend($.easing, { + easeOutBounce: function(x, t, b, c, d) { + if ((t /= d) < (1 / 2.75)) { + return c * (7.5625 * t * t) + b; + } else if (t < (2 / 2.75)) { + return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b; + } else if (t < (2.5 / 2.75)) { + return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b; + } else { + return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b; + } + }, + }); + } + + + function Timer(fn, delay) { + var startTime, + self = this, + timer, + _fn = fn, + _args = arguments, + _delay = delay; + + this.running = false; + + this.onpause = function() {}; + this.onresume = function() {}; + + this.cancel = function() { + this.running = false; + clearTimeout(timer); + }; + + this.pause = function() { + if (this.running) { + delay -= new Date().getTime() - startTime; + this.cancel(); + this.onpause(); + } + }; + + this.resume = function() { + if (!this.running) { + this.running = true; + startTime = new Date().getTime(); + + timer = setTimeout(function() { + _fn.apply(self, Array.prototype.slice.call(_args, 2, _args.length)); //Execute function with initial arguments, removing (fn & delay) + }, delay); + + this.onresume(); + } + }; + + this.reset = function() { + this.cancel(); + this.running = true; + delay = _delay; + timer = setTimeout(function() { + _fn.apply(self, Array.prototype.slice.call(_args, 2, _args.length)); //Execute function with initial arguments, removing (fn & delay) + }, _delay); + }; + + this.add = function(extraDelay) { + this.pause(); + delay += extraDelay; + this.resume(); + }; + + this.resume(); + } + + + /** + * @desc PUBLIC - Makes Slot Machine animation effect + * @param DOM element - Html element + * @param object settings - Plugin configuration params + * @return jQuery node - Returns jQuery selector with some new functions (shuffle, stop, next, auto, active) + */ + function SlotMachine(element, options) { + this.element = element; + this.settings = $.extend({}, defaults, options); + this.defaults = defaults; + this.name = pluginName; + + //jQuery selector + this.$slot = $(element); + //Slot Machine elements + this.$tiles = this.$slot.children(); + //Container to wrap $tiles + this.$container = null; + //Min marginTop offset + this._minTop = null; + //Max marginTop offset + this._maxTop = null; + //First element (the last of the html container) + this._$fakeFirstTile = null; + //Last element (the first of the html container) + this._$fakeLastTile = null; + //Timeout recursive function to handle auto (settings.auto) + this._timer = null; + //Callback function + this._oncompleteStack = [this.settings.complete]; + //Number of spins left before stop + this._spinsLeft = null; + //Future result + this.futureActive = null; + //Machine is running? + this.isRunning = false; + //Machine is stopping? + this.isStopping = false; + //Current active element + this.active = this.settings.active; + + this.$slot.css('overflow', 'hidden'); + + //Validate active index + if (this.settings.active < 0 || this.settings.active >= this.$tiles.length) { + this.settings.active = 0; + this.active = 0; + } + + //Wrap elements inside $container + this.$container = this.$tiles.wrapAll('
').parent(); + + //Set max top offset + this._maxTop = -this.$container.height(); + + //Add the last element behind the first to prevent the jump effect + this._$fakeFirstTile = this.$tiles.last().clone(); + this._$fakeLastTile = this.$tiles.first().clone(); + + this.$container.prepend(this._$fakeFirstTile); + this.$container.append(this._$fakeLastTile); + + //Set min top offset + this._minTop = -this._$fakeFirstTile.outerHeight(); + + //Show active element + this.$container.css('margin-top', this.getTileOffset(this.active)); + + //Start auto animation + if (this.settings.auto !== false) { + if (this.settings.auto === true) { + this.shuffle(); + } else { + this.auto(); + } + } + } + + /** + * @desc PRIVATE - Get element offset top + * @param int index - Element position + * @return int - Negative offset in px + */ + SlotMachine.prototype.getTileOffset = function(index) { + var offset = 0; + for (var i = 0; i < index; i++) { + offset += this.$tiles.eq(i).outerHeight(); + } + return -offset + this._minTop; + }; + + /** + * @desc PRIVATE - Get current showing element index + * @return int - Element index + */ + SlotMachine.prototype.getVisibleTile = function() { + var firstTileHeight = this.$tiles.first().height(), + containerMarginTop = parseInt(this.$container.css('margin-top').replace(/px/, ''), 10); + + return Math.abs(Math.round(containerMarginTop / firstTileHeight)) - 1; + }; + + /** + * @desc PUBLIC - Changes randomize function + * @param function|int - Set new randomize function + */ + SlotMachine.prototype.setRandomize = function(rnd) { + if (typeof rnd === 'number') { + this.settings.randomize = function() { + return rnd; + }; + } else { + this.settings.randomize = rnd; + } + }; + + /** + * @desc PRIVATE - Get random element different than last shown + * @param boolean cantBeTheCurrent - true||undefined if cant be choosen the current element, prevents repeat + * @return int - Element index + */ + SlotMachine.prototype.getRandom = function(cantBeTheCurrent) { + var rnd, + removePrevious = cantBeTheCurrent || false; + do { + rnd = Math.floor(Math.random() * this.$tiles.length); + } while ((removePrevious && rnd === this.active) && rnd >= 0); + + return rnd; + }; + + /** + * @desc PRIVATE - Get random element based on the custom randomize function + * @return int - Element index + */ + SlotMachine.prototype.getCustom = function() { + var choosen; + if (this.settings.randomize !== null && typeof this.settings.randomize === 'function') { + var index = this.settings.randomize.apply(this, [this.active]); + if (index < 0 || index >= this.$tiles.length) { + index = 0; + } + choosen = index; + } else { + choosen = this.getRandom(); + } + return choosen; + }; + + /** + * @desc PRIVATE - Get the previous element + * @return int - Element index + */ + SlotMachine.prototype.getPrev = function() { + var prevIndex = (this.active - 1 < 0) ? (this.$tiles.length - 1) : (this.active - 1); + return prevIndex; + }; + + /** + * @desc PRIVATE - Get the next element + * @return int - Element index + */ + SlotMachine.prototype.getNext = function() { + var nextIndex = (this.active + 1 < this.$tiles.length) ? (this.active + 1) : 0; + return nextIndex; + }; + + /** + * @desc PRIVATE - Set CSS classes to make speed effect + * @param string FX_SPEED - Element speed [FX_FAST_BLUR||FX_NORMAL_BLUR||FX_SLOW_BLUR||FX_STOP] + * @param string||boolean fade - Set fade gradient effect + */ + SlotMachine.prototype._setAnimationFX = function(FX_SPEED, fade) { + var self = this; + + setTimeout(function() { + self.$tiles.removeClass([FX_FAST, FX_NORMAL, FX_SLOW].join(' ')).addClass(FX_SPEED); + + if (fade !== true || FX_SPEED === FX_STOP) { + self.$slot.add(self.$tiles).removeClass(FX_GRADIENT); + } else { + self.$slot.add(self.$tiles).addClass(FX_GRADIENT); + } + }, this.settings.delay / 4); + }; + + /** + * @desc PRIVATE - Reset active element position + */ + SlotMachine.prototype._resetPosition = function() { + this.$container.css('margin-top', this.getTileOffset(this.active)); + }; + + /** + * @desc PRIVATE - Checks if the machine is on the screen + * @return int - Returns true if machine is on the screen + */ + SlotMachine.prototype.isVisible = function() { + //Stop animation if element is [above||below] screen, best for performance + var above = this.$slot.offset().top > $(window).scrollTop() + $(window).height(), + below = $(window).scrollTop() > this.$slot.height() + this.$slot.offset().top; + + return !above && !below; + }; + + /** + * @desc PUBLIC - SELECT previous element relative to the current active element + * @return int - Returns result index + */ + SlotMachine.prototype.prev = function() { + this.futureActive = this.getPrev(); + this.isRunning = true; + this.stop(false); + return this.futureActive; + }; + + /** + * @desc PUBLIC - SELECT next element relative to the current active element + * @return int - Returns result index + */ + SlotMachine.prototype.next = function() { + this.futureActive = this.getNext(); + this.isRunning = true; + this.stop(false); + return this.futureActive; + }; + + /** + * @desc PRIVATE - Starts shuffling the elements + * @param int repeations - Number of shuffles (undefined to make infinite animation + * @return int - Returns result index + */ + SlotMachine.prototype.shuffle = function(spins, onComplete) { + var self = this; + + if (onComplete !== undefined) { + this._oncompleteStack[1] = onComplete; + } + + this.isRunning = true; + var delay = this.settings.delay; + + if (this.futureActive === null) { + //Get random or custom element + var rnd = this.getCustom(); + this.futureActive = rnd; + } + + //Decreasing spin + if (typeof spins === 'number') { + //Change delay and speed + switch (spins) { + case 1: + case 2: + this._setAnimationFX(FX_SLOW, true); + break; + case 3: + case 4: + this._setAnimationFX(FX_NORMAL, true); + delay /= 1.5; + break; + default: + this._setAnimationFX(FX_FAST, true); + delay /= 2; + } + //Infinite spin + } else { + //Set animation effects + this._setAnimationFX(FX_FAST, true); + delay /= 2; + } + + //Perform animation + if (!this.isVisible() && this.settings.stopHidden === true) { + spins = 0; + self.stop(); + } else { + this.$container.animate({ + marginTop: this._maxTop + }, delay, 'linear', function() { + //Reset top position + self.$container.css('margin-top', 0); + + if (spins - 1 <= 0) { + self.stop(); + } else { + //Repeat animation + self.shuffle(spins - 1); + } + }); + } + + return this.futureActive; + }; + + /** + * @desc PRIVATE - Stop shuffling the elements + * @return int - Returns result index + */ + SlotMachine.prototype.stop = function(showGradient) { + if (!this.isRunning) { + return; + } else if (this.isStopping) { + return this.futureActive; + } + var self = this; + + //Stop animation NOW!!!!!!! + this.$container.clearQueue().stop(true, false); + + this._setAnimationFX(FX_SLOW, showGradient === undefined ? true : showGradient); + + this.isRunning = true; + this.isStopping = true; + + //Set current active element + this.active = this.getVisibleTile(); + + //Check direction to prevent jumping + if (this.futureActive > this.active) { + //We are moving to the prev (first to last) + if (this.active === 0 && this.futureActive === this.$tiles.length - 1) { + this.$container.css('margin-top', this.getTileOffset(this.$tiles.length)); + } + } else { + //We are moving to the next (last to first) + if (this.active === this.$tiles.length - 1 && this.futureActive === 0) { + this.$container.css('margin-top', 0); + } + } + + //Update last choosen element index + this.active = this.futureActive; + + //Get delay + var delay = this.settings.delay * 3; + + //Perform animation + this.$container.animate({ + marginTop: this.getTileOffset(this.active) + }, delay, 'easeOutBounce', function() { + + self.isStopping = false; + self.isRunning = false; + self.futureActive = null; + + if (typeof self._oncompleteStack[0] === 'function') { + self._oncompleteStack[0].apply(self, [self.active]); + } + if (typeof self._oncompleteStack[1] === 'function') { + self._oncompleteStack[1].apply(self, [self.active]); + } + }); + + //Disable blur + setTimeout(function() { + self._setAnimationFX(FX_STOP, false); + }, delay / 1.75); + + return this.active; + }; + + /** + * @desc PRIVATE - Start auto shufflings, animation stops each 3 repeations. Then restart animation recursively + */ + SlotMachine.prototype.auto = function() { + var self = this; + + this._timer = new Timer(function() { + if (typeof self.settings.randomize !== 'function') { + self.futureActive = self.getNext(); + } + self.isRunning = true; + if (!self.isVisible() && self.settings.stopHidden === true) { + setTimeout(function() { + self._timer.reset(); + }, 500); + } else { + self.shuffle(self.settings.spins, function() { + self._timer.reset(); + }); + } + + }, this.settings.auto); + }; + + + + /* + * Create new plugin instance if needed and return it + */ + function _getInstance(element, options) { + var machine; + if (!$.data(element[0], 'plugin_' + pluginName)) { + machine = new SlotMachine(element, options); + $.data(element[0], 'plugin_' + pluginName, machine); + } else { + machine = $.data(element[0], 'plugin_' + pluginName); + } + return machine; + } + + /* + * Chainable instance + */ + $.fn[pluginName] = function(options) { + if (this.length === 1) { + return _getInstance(this, options); + } else { + var $els = this; + return $.map($els, function(el, index) { + var $el = $els.eq(index); + return _getInstance($el, options); + }); + } + }; + +})(jQuery, window, document); diff --git a/dist/jquery.slotmachine.min.js b/dist/jquery.slotmachine.min.js index e625a3e..d2128eb 100644 --- a/dist/jquery.slotmachine.min.js +++ b/dist/jquery.slotmachine.min.js @@ -1,4 +1,4 @@ /*! SlotMachine - v2.0.11 - 2015-07-21 * https://github.com/josex2r/jQuery-SlotMachine * Copyright (c) 2015 Jose Luis Represa; Licensed MIT */ -!function(a,b,c,d){function e(a,b){var c,d,e=this,f=a,g=arguments,h=b;this.running=!1,this.onpause=function(){},this.onresume=function(){},this.cancel=function(){this.running=!1,clearTimeout(d)},this.pause=function(){this.running&&(b-=(new Date).getTime()-c,this.cancel(),this.onpause())},this.resume=function(){this.running||(this.running=!0,c=(new Date).getTime(),d=setTimeout(function(){f.apply(e,Array.prototype.slice.call(g,2,g.length))},b),this.onresume())},this.reset=function(){this.cancel(),this.running=!0,b=h,d=setTimeout(function(){f.apply(e,Array.prototype.slice.call(g,2,g.length))},h)},this.add=function(a){this.pause(),b+=a,this.resume()},this.resume()}function f(b,c){this.element=b,this.settings=a.extend({},i,c),this.defaults=i,this.name=h,this.$slot=a(b),this.$tiles=this.$slot.children(),this.$container=null,this._minTop=null,this._maxTop=null,this._$fakeFirstTile=null,this._$fakeLastTile=null,this._timer=null,this._oncompleteStack=[this.settings.complete],this._spinsLeft=null,this.futureActive=null,this.isRunning=!1,this.isStopping=!1,this.active=this.settings.active,this.$slot.css("overflow","hidden"),(this.settings.active<0||this.settings.active>=this.$tiles.length)&&(this.settings.active=0,this.active=0),this.$tiles.wrapAll("
"),this.$container=this.$slot.find(".slotMachineContainer"),this._maxTop=-this.$container.height(),this._$fakeFirstTile=this.$tiles.last().clone(),this._$fakeLastTile=this.$tiles.first().clone(),this.$container.prepend(this._$fakeFirstTile),this.$container.append(this._$fakeLastTile),this._minTop=-this._$fakeFirstTile.outerHeight(),this.$container.css("margin-top",this.getTileOffset(this.active)),this.settings.auto!==!1&&(this.settings.auto===!0?this.shuffle():this.auto())}function g(b,c){var d;return a.data(b[0],"plugin_"+h)?d=a.data(b[0],"plugin_"+h):(d=new f(b,c),a.data(b[0],"plugin_"+h,d)),d}var h="slotMachine",i={active:0,delay:200,auto:!1,randomize:null,complete:null,stopHidden:!0},j="slotMachineBlurFast",k="slotMachineBlurMedium",l="slotMachineBlurSlow",m="slotMachineGradient",n=m;a(c).ready(function(){var b='#slotMachineBlurFilterFast',c='#slotMachineBlurFilterMedium',d='#slotMachineBlurFilterSlow',e='#slotMachineFadeMask';a("body").append("')}),"function"!=typeof a.easing.easeOutBounce&&a.extend(a.easing,{easeOutBounce:function(a,b,c,d,e){return(b/=e)<1/2.75?7.5625*d*b*b+c:2/2.75>b?d*(7.5625*(b-=1.5/2.75)*b+.75)+c:2.5/2.75>b?d*(7.5625*(b-=2.25/2.75)*b+.9375)+c:d*(7.5625*(b-=2.625/2.75)*b+.984375)+c}}),f.prototype.getTileOffset=function(b){for(var c=0,d=0;b>d;d++)c+=a(this.$tiles.get(d)).outerHeight();return-c+this._minTop},f.prototype.getVisibleTile=function(){var a=this.$tiles.first().height(),b=parseInt(this.$container.css("margin-top").replace(/px/,""),10);return Math.abs(Math.round(b/a))-1},f.prototype.setRandomize=function(a){if("number"==typeof a){var b=function(){return a};this.settings.randomize=b}else this.settings.randomize=a},f.prototype.getRandom=function(a){var b,c=a||!1;do b=Math.floor(Math.random()*this.$tiles.length);while(c&&b===this.active&&b>=0);return b},f.prototype.getCustom=function(){var a;if(null!==this.settings.randomize&&"function"==typeof this.settings.randomize){var b=this.settings.randomize.apply(this,[this.active]);(0>b||b>=this.$tiles.length)&&(b=0),a=b}else a=this.getRandom();return a},f.prototype.getPrev=function(){var a=this.active-1<0?this.$tiles.length-1:this.active-1;return a},f.prototype.getNext=function(){var a=this.active+1a(b).scrollTop()+a(b).height(),d=a(b).scrollTop()>this.$slot.height()+this.$slot.offset().top;return!c&&!d},f.prototype.prev=function(){return this.futureActive=this.getPrev(),this.isRunning=!0,this.stop(!1),this.futureActive},f.prototype.next=function(){return this.futureActive=this.getNext(),this.isRunning=!0,this.stop(!1),this.futureActive},f.prototype.shuffle=function(a,b){var c=this;b!==d&&(this._oncompleteStack[1]=b),this.isRunning=!0;var e=this.settings.delay;if(null===this.futureActive){var f=this.getCustom();this.futureActive=f}if("number"==typeof a)switch(a){case 1:case 2:this._setAnimationFX(l,!0);break;case 3:case 4:this._setAnimationFX(k,!0),e/=1.5;break;default:this._setAnimationFX(j,!0),e/=2}else this._setAnimationFX(j,!0),e/=2;return this.isVisible()||this.settings.stopHidden!==!0?this.$container.animate({marginTop:this._maxTop},e,"linear",function(){c.$container.css("margin-top",0),0>=a-1?c.stop():c.shuffle(a-1)}):(a=0,c.stop()),this.futureActive},f.prototype.stop=function(a){if(this.isRunning){if(this.isStopping)return this.futureActive;var b=this;this.$container.clearQueue().stop(!0,!1),this._setAnimationFX(l,a===d?!0:a),this.isRunning=!0,this.isStopping=!0,this.active=this.getVisibleTile(),this.futureActive>this.active?0===this.active&&this.futureActive===this.$tiles.length-1&&this.$container.css("margin-top",this.getTileOffset(this.$tiles.length)):this.active===this.$tiles.length-1&&0===this.futureActive&&this.$container.css("margin-top",0),this.active=this.futureActive;var c=3*this.settings.delay;return this.$container.animate({marginTop:this.getTileOffset(this.active)},c,"easeOutBounce",function(){b.isStopping=!1,b.isRunning=!1,b.futureActive=null,"function"==typeof b._oncompleteStack[0]&&b._oncompleteStack[0].apply(b,[b.active]),"function"==typeof b._oncompleteStack[1]&&b._oncompleteStack[1].apply(b,[b.active])}),setTimeout(function(){b._setAnimationFX(n,!1)},c/1.75),this.active}},f.prototype.auto=function(){var a=this;this._timer=new e(function(){"function"!=typeof a.settings.randomize&&(a.futureActive=a.getNext()),a.isRunning=!0,a.isVisible()||a.settings.stopHidden!==!0?a.shuffle(5,function(){a._timer.reset()}):setTimeout(function(){a._timer.reset()},500)},this.settings.auto)},a.fn[h]=function(b){return 1===this.length?g(this,b):this.each(function(){a.data(this,"plugin_"+h)||g(this,b)})}}(jQuery,window,document); \ No newline at end of file +!function(a,b,c,d){function e(a,b){var c,d,e=this,f=a,g=arguments,h=b;this.running=!1,this.onpause=function(){},this.onresume=function(){},this.cancel=function(){this.running=!1,clearTimeout(d)},this.pause=function(){this.running&&(b-=(new Date).getTime()-c,this.cancel(),this.onpause())},this.resume=function(){this.running||(this.running=!0,c=(new Date).getTime(),d=setTimeout(function(){f.apply(e,Array.prototype.slice.call(g,2,g.length))},b),this.onresume())},this.reset=function(){this.cancel(),this.running=!0,b=h,d=setTimeout(function(){f.apply(e,Array.prototype.slice.call(g,2,g.length))},h)},this.add=function(a){this.pause(),b+=a,this.resume()},this.resume()}function f(b,c){this.element=b,this.settings=a.extend({},i,c),this.defaults=i,this.name=h,this.$slot=a(b),this.$tiles=this.$slot.children(),this.$container=null,this._minTop=null,this._maxTop=null,this._$fakeFirstTile=null,this._$fakeLastTile=null,this._timer=null,this._oncompleteStack=[this.settings.complete],this._spinsLeft=null,this.futureActive=null,this.isRunning=!1,this.isStopping=!1,this.active=this.settings.active,this.$slot.css("overflow","hidden"),(this.settings.active<0||this.settings.active>=this.$tiles.length)&&(this.settings.active=0,this.active=0),this.$container=this.$tiles.wrapAll('
').parent(),this._maxTop=-this.$container.height(),this._$fakeFirstTile=this.$tiles.last().clone(),this._$fakeLastTile=this.$tiles.first().clone(),this.$container.prepend(this._$fakeFirstTile),this.$container.append(this._$fakeLastTile),this._minTop=-this._$fakeFirstTile.outerHeight(),this.$container.css("margin-top",this.getTileOffset(this.active)),this.settings.auto!==!1&&(this.settings.auto===!0?this.shuffle():this.auto())}function g(b,c){var d;return a.data(b[0],"plugin_"+h)?d=a.data(b[0],"plugin_"+h):(d=new f(b,c),a.data(b[0],"plugin_"+h,d)),d}var h="slotMachine",i={active:0,delay:200,auto:!1,spins:5,randomize:null,complete:null,stopHidden:!0},j="slotMachineBlurFast",k="slotMachineBlurMedium",l="slotMachineBlurSlow",m="slotMachineGradient",n=m;a(c).ready(function(){var b='#slotMachineBlurFilterFast',c='#slotMachineBlurFilterMedium',d='#slotMachineBlurFilterSlow',e='#slotMachineFadeMask';a("body").append("')}),"function"!=typeof a.easing.easeOutBounce&&a.extend(a.easing,{easeOutBounce:function(a,b,c,d,e){return(b/=e)<1/2.75?7.5625*d*b*b+c:2/2.75>b?d*(7.5625*(b-=1.5/2.75)*b+.75)+c:2.5/2.75>b?d*(7.5625*(b-=2.25/2.75)*b+.9375)+c:d*(7.5625*(b-=2.625/2.75)*b+.984375)+c}}),f.prototype.getTileOffset=function(a){for(var b=0,c=0;a>c;c++)b+=this.$tiles.eq(c).outerHeight();return-b+this._minTop},f.prototype.getVisibleTile=function(){var a=this.$tiles.first().height(),b=parseInt(this.$container.css("margin-top").replace(/px/,""),10);return Math.abs(Math.round(b/a))-1},f.prototype.setRandomize=function(a){this.settings.randomize="number"==typeof a?function(){return a}:a},f.prototype.getRandom=function(a){var b,c=a||!1;do b=Math.floor(Math.random()*this.$tiles.length);while(c&&b===this.active&&b>=0);return b},f.prototype.getCustom=function(){var a;if(null!==this.settings.randomize&&"function"==typeof this.settings.randomize){var b=this.settings.randomize.apply(this,[this.active]);(0>b||b>=this.$tiles.length)&&(b=0),a=b}else a=this.getRandom();return a},f.prototype.getPrev=function(){var a=this.active-1<0?this.$tiles.length-1:this.active-1;return a},f.prototype.getNext=function(){var a=this.active+1a(b).scrollTop()+a(b).height(),d=a(b).scrollTop()>this.$slot.height()+this.$slot.offset().top;return!c&&!d},f.prototype.prev=function(){return this.futureActive=this.getPrev(),this.isRunning=!0,this.stop(!1),this.futureActive},f.prototype.next=function(){return this.futureActive=this.getNext(),this.isRunning=!0,this.stop(!1),this.futureActive},f.prototype.shuffle=function(a,b){var c=this;b!==d&&(this._oncompleteStack[1]=b),this.isRunning=!0;var e=this.settings.delay;if(null===this.futureActive){var f=this.getCustom();this.futureActive=f}if("number"==typeof a)switch(a){case 1:case 2:this._setAnimationFX(l,!0);break;case 3:case 4:this._setAnimationFX(k,!0),e/=1.5;break;default:this._setAnimationFX(j,!0),e/=2}else this._setAnimationFX(j,!0),e/=2;return this.isVisible()||this.settings.stopHidden!==!0?this.$container.animate({marginTop:this._maxTop},e,"linear",function(){c.$container.css("margin-top",0),0>=a-1?c.stop():c.shuffle(a-1)}):(a=0,c.stop()),this.futureActive},f.prototype.stop=function(a){if(this.isRunning){if(this.isStopping)return this.futureActive;var b=this;this.$container.clearQueue().stop(!0,!1),this._setAnimationFX(l,a===d?!0:a),this.isRunning=!0,this.isStopping=!0,this.active=this.getVisibleTile(),this.futureActive>this.active?0===this.active&&this.futureActive===this.$tiles.length-1&&this.$container.css("margin-top",this.getTileOffset(this.$tiles.length)):this.active===this.$tiles.length-1&&0===this.futureActive&&this.$container.css("margin-top",0),this.active=this.futureActive;var c=3*this.settings.delay;return this.$container.animate({marginTop:this.getTileOffset(this.active)},c,"easeOutBounce",function(){b.isStopping=!1,b.isRunning=!1,b.futureActive=null,"function"==typeof b._oncompleteStack[0]&&b._oncompleteStack[0].apply(b,[b.active]),"function"==typeof b._oncompleteStack[1]&&b._oncompleteStack[1].apply(b,[b.active])}),setTimeout(function(){b._setAnimationFX(n,!1)},c/1.75),this.active}},f.prototype.auto=function(){var a=this;this._timer=new e(function(){"function"!=typeof a.settings.randomize&&(a.futureActive=a.getNext()),a.isRunning=!0,a.isVisible()||a.settings.stopHidden!==!0?a.shuffle(a.settings.spins,function(){a._timer.reset()}):setTimeout(function(){a._timer.reset()},500)},this.settings.auto)},a.fn[h]=function(b){if(1===this.length)return g(this,b);var c=this;return a.map(c,function(a,d){var e=c.eq(d);return g(e,b)})}}(jQuery,window,document); \ No newline at end of file diff --git a/src/jquery.slotmachine.js b/src/jquery.slotmachine.js index 6e83fb7..881a207 100644 --- a/src/jquery.slotmachine.js +++ b/src/jquery.slotmachine.js @@ -556,10 +556,10 @@ if (this.length === 1) { return _getInstance(this, options); } else { - return this.each(function() { - if (!$.data(this, 'plugin_' + pluginName)) { - _getInstance(this, options); - } + var $els = this; + return $.map($els, function(el, index) { + var $el = $els.eq(index); + return _getInstance($el, options); }); } };