├── README.md ├── ezslots.css ├── ezslots.js ├── images ├── banana.png ├── cherries.png ├── lemon.png ├── orange.png ├── plum.png ├── seven.png └── watermelon.png ├── jquery-1.11.3.js ├── jquery.easing.1.3.js └── sample.html /README.md: -------------------------------------------------------------------------------- 1 | ezSlots 2 | ======= 3 | ezSlots is a trivially simple JQuery "Slot Machine Construction Kit". Its only dependencies are jQuery and the jQuery Easing library. It can be allowed to run randomly or to go to a pre-specified "winning" combo, can have any number of wheels, and use any set of symbols (HTML characters, images, etc) 4 | 5 | It is designed as an educational tool / sample as well as a "ready to go" piece of technology to add some slots to any page. There are other, fancier jQuery slot machines out there (including the ability to keep spinning indefinitely, which ezSlots does not have) but by keeping the code base to under 100 lines, ezSlots is meant to be easy to understand and customize. 6 | 7 | Minimal Usage 8 | ----- 9 | 10 | 11 | 12 | 13 | 19 |
20 | 21 | Options 22 | ------- 23 | The first argument is the id of a div existing on the page. 24 | 25 | The second argument is a map with the following keys: 26 | 27 | - reelCount - how many reels/windows to display (default:3) 28 | - symbols - an array containing html strings (or an array of arrays if you want different symbol sets for each reel) (default:['A','B','C'] 29 | - winningSet - an optional array specifying the offset of a "winning" spin. (For example, if the first entry in the symbols array was the "winner", this should be [0,0,0]) (default: none. If you want to call .win() this should be explicitly set) 30 | - startingSet - an optional array specifying the offset when the slots first appear. (default:none, so just random entries) 31 | - width - width of each slot window in pixels (default: 100) 32 | - height - height of each slot window in pixels (default: 100) 33 | - callback - optional function to be called upon completion of animation (approximately). Will be passed the results. 34 | 35 | Samples 36 | ------- 37 | There is a [sample page](http://kirkdev.alienbill.com/ezslots/sample.html) where you can see the "all defaults version", one with simple text numbers, one with images, and one that uses mixed type. The mixed type example uses a callback function to show a JSON stringified version of the results. 38 | 39 | Understanding the Code 40 | ---------------------- 41 | The code starts with the pattern as storing "this" in a private variable "that". It then sets member variables with a mix of values from the options map and hard-wired defaults. 42 | 43 | The init function is defined but only called once the other member functions are set. It creates the appropriate number of window/reels. 44 | 45 | The CSS suggest the structure ezslots generates: 46 | 47 | .ezslots>.window>.slider>.symbol>.content 48 | 49 | * ezslots - a class assigned to the containing div 50 | * window - refers one of the reel-viewing windows 51 | * slider - this is the part that actually moves. 52 | * symbol - container for a single symbol that will show up in the window 53 | * content - where the symbol actually "lives" (needed for horizontal and vertical centering) 54 | 55 | Heights and width are manually and redundantly applied to ensure consistency with the animation. 56 | 57 | Also, each reel window is given the class "window_#" where # is its position in the ezslots div. 58 | 59 | For the animation effect, 20 symbols are added to each window (randomly selected from the array of options for that reel, except possibly for the "winning" result that actually shows up in the window when things come to rest) and an "easeOutElastic" easing gives the illusion of a rotating reel that then settles into place. 60 | 61 | With a regular or guaranteed win, the results are return an array of offsets into the array of "possibilities." There's currently no function provided for translating these offsets into the html strings being displayed, but it would be trivial to write if needed. -------------------------------------------------------------------------------- /ezslots.css: -------------------------------------------------------------------------------- 1 | .ezslots>.window{ 2 | overflow:hidden; 3 | display:inline-block; 4 | } 5 | .ezslots>.window>.slider>.symbol{ 6 | text-align:center; 7 | display:table; 8 | } 9 | .ezslots>.window>.slider>.symbol>.content{ 10 | padding:0px; 11 | margin:0px; 12 | display:table-cell; 13 | text-align:center; 14 | vertical-align:middle; 15 | } 16 | -------------------------------------------------------------------------------- /ezslots.js: -------------------------------------------------------------------------------- 1 | function EZSlots(id,useroptions){ 2 | var that = this; //keep reference to function for use in callbacks 3 | //set some variables from the options, or with defaults. 4 | var options = useroptions ? useroptions : {}; 5 | this.reelCount = options.reelCount ? options.reelCount : 3; //how many reels, assume 3 6 | this.symbols = options.symbols ? options.symbols : ['A','B','C']; 7 | this.sameSymbolsEachSlot = true; 8 | this.startingSet = options.startingSet; 9 | this.winningSet = options.winningSet; 10 | this.width = options.width ? options.width : 100; 11 | this.height = options.width ? options.height : 100; 12 | this.time = options.time ? (options.time * 1000) : 6500; //time in millis for a spin to take 13 | this.howManySymbolsToAppend = Math.round(this.time/325); //how many symbols each spin adds 14 | this.endingLocation = 7; //location for selected symbol... needs to be a few smaller than howManySymbolsToAppend 15 | this.jqo = $("#"+id); //jquery object reference to main wrapper 16 | this.jqoSliders = []; //jquery object reference to strips sliding up and down 17 | this.callback = options.callback; //callback function to be called once slots animation is finished 18 | 19 | //to initialize we construct the correct number of slot windows 20 | //and then populate each strip once 21 | this.init = function(){ 22 | this.jqo.addClass("ezslots"); //to get the css goodness 23 | //figure out if we are using the same of symbols for each window - assume if the first 24 | //entry of the symbols is not a string we have an array of arrays 25 | if(typeof this.symbols[0] != 'string'){ 26 | this.sameSymbolsEachSlot = false; 27 | } 28 | //make each slot window 29 | for(var i = 0; i < this.reelCount; i++){ 30 | var jqoSlider = $('
'); 31 | var jqoWindow = $('
'); 32 | this.scaleJqo(jqoWindow).append(jqoSlider); //make window right size and put slider in it 33 | this.jqo.append(jqoWindow); //add window to main div 34 | this.jqoSliders.push(jqoSlider); //keep reference to jqo of slider 35 | this.addSymbolsToStrip(jqoSlider,i, false, true); //and add the initial set 36 | } 37 | }; 38 | //convenience function since we need to apply width and height to multiple parts 39 | this.scaleJqo = function(jqo){ 40 | jqo.css("height",this.height+"px").css("width",this.width+"px"); 41 | return jqo; 42 | } 43 | //add the various symbols - but take care to possibly add the "winner" as the symbol chosen 44 | this.addSymbolsToStrip = function(jqoSlider, whichReel, shouldWin, isInitialCall){ 45 | var symbolsToUse = that.sameSymbolsEachSlot ? that.symbols : that.symbols[whichReel]; 46 | var chosen = shouldWin ? that.winningSet[whichReel] : Math.floor(Math.random()*symbolsToUse.length); 47 | for(var i = 0; i < that.howManySymbolsToAppend; i++){ 48 | var ctr = (i == that.endingLocation) ? chosen : Math.floor(Math.random()*symbolsToUse.length); 49 | if(i == 0 && isInitialCall && that.startingSet){ 50 | ctr = that.startingSet[whichReel]; 51 | } 52 | //we nest "content" inside of "symbol" so we can do vertical and horizontal centering more easily 53 | var jqoContent = $("
"+symbolsToUse[ctr]+"
"); 54 | that.scaleJqo(jqoContent); 55 | var jqoSymbol = $("
"); 56 | that.scaleJqo(jqoSymbol); 57 | jqoSymbol.append(jqoContent); 58 | jqoSlider.append(jqoSymbol); 59 | } 60 | return chosen; 61 | } 62 | //to spin, we add symbols to a strip, and then bounce it down 63 | this.spinOne = function(jqoSlider,whichReel,shouldWin){ 64 | var heightBefore = parseInt(jqoSlider.css("height"), 10); 65 | var chosen = that.addSymbolsToStrip(jqoSlider,whichReel,shouldWin); 66 | var marginTop = -(heightBefore + ((that.endingLocation) * that.height)); 67 | jqoSlider.stop(true,true).animate( 68 | {"margin-top":marginTop+"px"}, 69 | {'duration' : that.time + Math.round(Math.random()*1000), 'easing' : "easeOutElastic"}); 70 | return chosen; 71 | } 72 | 73 | this.spinAll = function(shouldWin){ 74 | var results = []; 75 | for(var i = 0; i < that.reelCount; i++){ 76 | results.push(that.spinOne(that.jqoSliders[i],i,shouldWin)); 77 | } 78 | 79 | if(that.callback) { 80 | setTimeout(function(){ 81 | that.callback(results); 82 | }, that.time); 83 | } 84 | 85 | return results; 86 | } 87 | 88 | this.init(); 89 | return { 90 | spin : function(){ 91 | return that.spinAll(); 92 | }, 93 | win : function(){ 94 | return that.spinAll(true); 95 | } 96 | } 97 | } 98 | 99 | 100 | -------------------------------------------------------------------------------- /images/banana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkjerk/ezslots/f57896c5cefe9a5979b89eb2bec506f93146580d/images/banana.png -------------------------------------------------------------------------------- /images/cherries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkjerk/ezslots/f57896c5cefe9a5979b89eb2bec506f93146580d/images/cherries.png -------------------------------------------------------------------------------- /images/lemon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkjerk/ezslots/f57896c5cefe9a5979b89eb2bec506f93146580d/images/lemon.png -------------------------------------------------------------------------------- /images/orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkjerk/ezslots/f57896c5cefe9a5979b89eb2bec506f93146580d/images/orange.png -------------------------------------------------------------------------------- /images/plum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkjerk/ezslots/f57896c5cefe9a5979b89eb2bec506f93146580d/images/plum.png -------------------------------------------------------------------------------- /images/seven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkjerk/ezslots/f57896c5cefe9a5979b89eb2bec506f93146580d/images/seven.png -------------------------------------------------------------------------------- /images/watermelon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkjerk/ezslots/f57896c5cefe9a5979b89eb2bec506f93146580d/images/watermelon.png -------------------------------------------------------------------------------- /jquery.easing.1.3.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 3 | * 4 | * Uses the built in easing capabilities added In jQuery 1.1 5 | * to offer multiple easing options 6 | * 7 | * TERMS OF USE - jQuery Easing 8 | * 9 | * Open source under the BSD License. 10 | * 11 | * Copyright © 2008 George McGinley Smith 12 | * All rights reserved. 13 | * 14 | * Redistribution and use in source and binary forms, with or without modification, 15 | * are permitted provided that the following conditions are met: 16 | * 17 | * Redistributions of source code must retain the above copyright notice, this list of 18 | * conditions and the following disclaimer. 19 | * Redistributions in binary form must reproduce the above copyright notice, this list 20 | * of conditions and the following disclaimer in the documentation and/or other materials 21 | * provided with the distribution. 22 | * 23 | * Neither the name of the author nor the names of contributors may be used to endorse 24 | * or promote products derived from this software without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 27 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 28 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 29 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 31 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | */ 37 | 38 | // t: current time, b: begInnIng value, c: change In value, d: duration 39 | jQuery.easing['jswing'] = jQuery.easing['swing']; 40 | 41 | jQuery.extend( jQuery.easing, 42 | { 43 | def: 'easeOutQuad', 44 | swing: function (x, t, b, c, d) { 45 | //alert(jQuery.easing.default); 46 | return jQuery.easing[jQuery.easing.def](x, t, b, c, d); 47 | }, 48 | easeInQuad: function (x, t, b, c, d) { 49 | return c*(t/=d)*t + b; 50 | }, 51 | easeOutQuad: function (x, t, b, c, d) { 52 | return -c *(t/=d)*(t-2) + b; 53 | }, 54 | easeInOutQuad: function (x, t, b, c, d) { 55 | if ((t/=d/2) < 1) return c/2*t*t + b; 56 | return -c/2 * ((--t)*(t-2) - 1) + b; 57 | }, 58 | easeInCubic: function (x, t, b, c, d) { 59 | return c*(t/=d)*t*t + b; 60 | }, 61 | easeOutCubic: function (x, t, b, c, d) { 62 | return c*((t=t/d-1)*t*t + 1) + b; 63 | }, 64 | easeInOutCubic: function (x, t, b, c, d) { 65 | if ((t/=d/2) < 1) return c/2*t*t*t + b; 66 | return c/2*((t-=2)*t*t + 2) + b; 67 | }, 68 | easeInQuart: function (x, t, b, c, d) { 69 | return c*(t/=d)*t*t*t + b; 70 | }, 71 | easeOutQuart: function (x, t, b, c, d) { 72 | return -c * ((t=t/d-1)*t*t*t - 1) + b; 73 | }, 74 | easeInOutQuart: function (x, t, b, c, d) { 75 | if ((t/=d/2) < 1) return c/2*t*t*t*t + b; 76 | return -c/2 * ((t-=2)*t*t*t - 2) + b; 77 | }, 78 | easeInQuint: function (x, t, b, c, d) { 79 | return c*(t/=d)*t*t*t*t + b; 80 | }, 81 | easeOutQuint: function (x, t, b, c, d) { 82 | return c*((t=t/d-1)*t*t*t*t + 1) + b; 83 | }, 84 | easeInOutQuint: function (x, t, b, c, d) { 85 | if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; 86 | return c/2*((t-=2)*t*t*t*t + 2) + b; 87 | }, 88 | easeInSine: function (x, t, b, c, d) { 89 | return -c * Math.cos(t/d * (Math.PI/2)) + c + b; 90 | }, 91 | easeOutSine: function (x, t, b, c, d) { 92 | return c * Math.sin(t/d * (Math.PI/2)) + b; 93 | }, 94 | easeInOutSine: function (x, t, b, c, d) { 95 | return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; 96 | }, 97 | easeInExpo: function (x, t, b, c, d) { 98 | return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; 99 | }, 100 | easeOutExpo: function (x, t, b, c, d) { 101 | return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; 102 | }, 103 | easeInOutExpo: function (x, t, b, c, d) { 104 | if (t==0) return b; 105 | if (t==d) return b+c; 106 | if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; 107 | return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; 108 | }, 109 | easeInCirc: function (x, t, b, c, d) { 110 | return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; 111 | }, 112 | easeOutCirc: function (x, t, b, c, d) { 113 | return c * Math.sqrt(1 - (t=t/d-1)*t) + b; 114 | }, 115 | easeInOutCirc: function (x, t, b, c, d) { 116 | if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; 117 | return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; 118 | }, 119 | easeInElastic: function (x, t, b, c, d) { 120 | var s=1.70158;var p=0;var a=c; 121 | if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 122 | if (a < Math.abs(c)) { a=c; var s=p/4; } 123 | else var s = p/(2*Math.PI) * Math.asin (c/a); 124 | return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 125 | }, 126 | easeOutElastic: function (x, t, b, c, d) { 127 | var s=1.70158;var p=0;var a=c; 128 | if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 129 | if (a < Math.abs(c)) { a=c; var s=p/4; } 130 | else var s = p/(2*Math.PI) * Math.asin (c/a); 131 | return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; 132 | }, 133 | easeInOutElastic: function (x, t, b, c, d) { 134 | var s=1.70158;var p=0;var a=c; 135 | if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); 136 | if (a < Math.abs(c)) { a=c; var s=p/4; } 137 | else var s = p/(2*Math.PI) * Math.asin (c/a); 138 | if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 139 | return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; 140 | }, 141 | easeInBack: function (x, t, b, c, d, s) { 142 | if (s == undefined) s = 1.70158; 143 | return c*(t/=d)*t*((s+1)*t - s) + b; 144 | }, 145 | easeOutBack: function (x, t, b, c, d, s) { 146 | if (s == undefined) s = 1.70158; 147 | return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; 148 | }, 149 | easeInOutBack: function (x, t, b, c, d, s) { 150 | if (s == undefined) s = 1.70158; 151 | if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; 152 | return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; 153 | }, 154 | easeInBounce: function (x, t, b, c, d) { 155 | return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; 156 | }, 157 | easeOutBounce: function (x, t, b, c, d) { 158 | if ((t/=d) < (1/2.75)) { 159 | return c*(7.5625*t*t) + b; 160 | } else if (t < (2/2.75)) { 161 | return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; 162 | } else if (t < (2.5/2.75)) { 163 | return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; 164 | } else { 165 | return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; 166 | } 167 | }, 168 | easeInOutBounce: function (x, t, b, c, d) { 169 | if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; 170 | return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; 171 | } 172 | }); 173 | 174 | /* 175 | * 176 | * TERMS OF USE - EASING EQUATIONS 177 | * 178 | * Open source under the BSD License. 179 | * 180 | * Copyright © 2001 Robert Penner 181 | * All rights reserved. 182 | * 183 | * Redistribution and use in source and binary forms, with or without modification, 184 | * are permitted provided that the following conditions are met: 185 | * 186 | * Redistributions of source code must retain the above copyright notice, this list of 187 | * conditions and the following disclaimer. 188 | * Redistributions in binary form must reproduce the above copyright notice, this list 189 | * of conditions and the following disclaimer in the documentation and/or other materials 190 | * provided with the distribution. 191 | * 192 | * Neither the name of the author nor the names of contributors may be used to endorse 193 | * or promote products derived from this software without specific prior written permission. 194 | * 195 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 196 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 197 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 198 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 199 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 200 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 201 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 202 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 203 | * OF THE POSSIBILITY OF SUCH DAMAGE. 204 | * 205 | */ -------------------------------------------------------------------------------- /sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 18 |

all defaults

19 |
20 | 21 | 22 |

simple numbers with winning set

23 |
24 | 25 | 26 | 27 |

simple numbers but longer spin

28 |
29 | 30 | 31 | 32 |

images and more reels

33 |
34 | 35 | 36 | 37 |

mixed reel types

38 |
39 | 40 | 41 | 42 | 43 | 79 | 80 | --------------------------------------------------------------------------------