/* * jQuery Slot Machine v1.0.0 * https://github.com/josex2r/jQuery-SlotMachine * * Copyright 2014 Jose Luis Represa * Released under the MIT license */ (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(); }; 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; //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 = - 1560; //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 = - 120; //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; } var self = this; //Stop animation NOW!!!!!!! this.$container.clearQueue().stop(true, false); this._setAnimationFX(FX_SLOW, showGradient === undefined ? true : showGradient); this.isRunning = 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; this.futureActive = null; //Get delay var delay = this.settings.delay * 3; //Perform animation this.$container.animate({ marginTop : this.getTileOffset(this.active) }, delay, 'easeOutBounce', function (){ self.isRunning = false; //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); } }); } }; })( window.SlotMachine.jQuery, window, document );