ripples.js (7360B)
1 /* Copyright 2014+, Federico Zivolo, LICENSE at https://github.com/FezVrasta/bootstrap-material-design/blob/master/LICENSE.md */ 2 /* globals jQuery, navigator */ 3 4 (function($, window, document, undefined) { 5 6 "use strict"; 7 8 /** 9 * Define the name of the plugin 10 */ 11 var ripples = "ripples"; 12 13 14 /** 15 * Get an instance of the plugin 16 */ 17 var self = null; 18 19 20 /** 21 * Define the defaults of the plugin 22 */ 23 var defaults = {}; 24 25 26 /** 27 * Create the main plugin function 28 */ 29 function Ripples(element, options) { 30 self = this; 31 32 this.element = $(element); 33 34 this.options = $.extend({}, defaults, options); 35 36 this._defaults = defaults; 37 this._name = ripples; 38 39 this.init(); 40 } 41 42 43 /** 44 * Initialize the plugin 45 */ 46 Ripples.prototype.init = function() { 47 var $element = this.element; 48 49 $element.on("mousedown touchstart", function(event) { 50 /** 51 * Verify if the user is just touching on a device and return if so 52 */ 53 if(self.isTouch() && event.type === "mousedown") { 54 return; 55 } 56 57 58 /** 59 * Verify if the current element already has a ripple wrapper element and 60 * creates if it doesn't 61 */ 62 if(!($element.find(".ripple-wrapper").length)) { 63 $element.append("<div class=\"ripple-wrapper\"></div>"); 64 } 65 66 67 /** 68 * Find the ripple wrapper 69 */ 70 var $wrapper = $element.children(".ripple-wrapper"); 71 72 73 /** 74 * Get relY and relX positions 75 */ 76 var relY = self.getRelY($wrapper, event); 77 var relX = self.getRelX($wrapper, event); 78 79 80 /** 81 * If relY and/or relX are false, return the event 82 */ 83 if(!relY && !relX) { 84 return; 85 } 86 87 88 /** 89 * Get the ripple color 90 */ 91 var rippleColor = self.getRipplesColor($element); 92 93 94 /** 95 * Create the ripple element 96 */ 97 var $ripple = $("<div></div>"); 98 99 $ripple 100 .addClass("ripple") 101 .css({ 102 "left": relX, 103 "top": relY, 104 "background-color": rippleColor 105 }); 106 107 108 /** 109 * Append the ripple to the wrapper 110 */ 111 $wrapper.append($ripple); 112 113 114 /** 115 * Make sure the ripple has the styles applied (ugly hack but it works) 116 */ 117 (function() { return window.getComputedStyle($ripple[0]).opacity; })(); 118 119 120 /** 121 * Turn on the ripple animation 122 */ 123 self.rippleOn($element, $ripple); 124 125 126 /** 127 * Call the rippleEnd function when the transition "on" ends 128 */ 129 setTimeout(function() { 130 self.rippleEnd($ripple); 131 }, 500); 132 133 134 /** 135 * Detect when the user leaves the element 136 */ 137 $element.on("mouseup mouseleave touchend", function() { 138 $ripple.data("mousedown", "off"); 139 140 if($ripple.data("animating") === "off") { 141 self.rippleOut($ripple); 142 } 143 }); 144 145 }); 146 }; 147 148 149 /** 150 * Get the new size based on the element height/width and the ripple width 151 */ 152 Ripples.prototype.getNewSize = function($element, $ripple) { 153 154 return (Math.max($element.outerWidth(), $element.outerHeight()) / $ripple.outerWidth()) * 2.5; 155 }; 156 157 158 /** 159 * Get the relX 160 */ 161 Ripples.prototype.getRelX = function($wrapper, event) { 162 var wrapperOffset = $wrapper.offset(); 163 164 if(!self.isTouch()) { 165 /** 166 * Get the mouse position relative to the ripple wrapper 167 */ 168 return event.pageX - wrapperOffset.left; 169 } else { 170 /** 171 * Make sure the user is using only one finger and then get the touch 172 * position relative to the ripple wrapper 173 */ 174 event = event.originalEvent; 175 176 if(event.touches.length !== 1) { 177 return event.touches[0].pageX - wrapperOffset.left; 178 } 179 180 return false; 181 } 182 }; 183 184 185 /** 186 * Get the relY 187 */ 188 Ripples.prototype.getRelY = function($wrapper, event) { 189 var wrapperOffset = $wrapper.offset(); 190 191 if(!self.isTouch()) { 192 /** 193 * Get the mouse position relative to the ripple wrapper 194 */ 195 return event.pageY - wrapperOffset.top; 196 } else { 197 /** 198 * Make sure the user is using only one finger and then get the touch 199 * position relative to the ripple wrapper 200 */ 201 event = event.originalEvent; 202 203 if(event.touches.length !== 1) { 204 return event.touches[0].pageY - wrapperOffset.top; 205 } 206 207 return false; 208 } 209 }; 210 211 212 /** 213 * Get the ripple color 214 */ 215 Ripples.prototype.getRipplesColor = function($element) { 216 217 var color = $element.data("ripple-color") ? $element.data("ripple-color") : window.getComputedStyle($element[0]).color; 218 219 return color; 220 }; 221 222 223 /** 224 * Verify if the client browser has transistion support 225 */ 226 Ripples.prototype.hasTransitionSupport = function() { 227 var thisBody = document.body || document.documentElement; 228 var thisStyle = thisBody.style; 229 230 var support = ( 231 thisStyle.transition !== undefined || 232 thisStyle.WebkitTransition !== undefined || 233 thisStyle.MozTransition !== undefined || 234 thisStyle.MsTransition !== undefined || 235 thisStyle.OTransition !== undefined 236 ); 237 238 return support; 239 }; 240 241 242 /** 243 * Verify if the client is using a mobile device 244 */ 245 Ripples.prototype.isTouch = function() { 246 return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); 247 }; 248 249 250 /** 251 * End the animation of the ripple 252 */ 253 Ripples.prototype.rippleEnd = function($ripple) { 254 $ripple.data("animating", "off"); 255 256 if($ripple.data("mousedown") === "off") { 257 self.rippleOut($ripple); 258 } 259 }; 260 261 262 /** 263 * Turn off the ripple effect 264 */ 265 Ripples.prototype.rippleOut = function($ripple) { 266 $ripple.off(); 267 268 if(self.hasTransitionSupport()) { 269 $ripple.addClass("ripple-out"); 270 } else { 271 $ripple.animate({"opacity": 0}, 100, function() { 272 $ripple.trigger("transitionend"); 273 }); 274 } 275 276 $ripple.on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function() { 277 $ripple.remove(); 278 }); 279 }; 280 281 282 /** 283 * Turn on the ripple effect 284 */ 285 Ripples.prototype.rippleOn = function($element, $ripple) { 286 var size = self.getNewSize($element, $ripple); 287 288 if(self.hasTransitionSupport()) { 289 $ripple 290 .css({ 291 "-ms-transform": "scale(" + size + ")", 292 "-moz-transform": "scale(" + size + ")", 293 "-webkit-transform": "scale(" + size + ")", 294 "transform": "scale(" + size + ")" 295 }) 296 .addClass("ripple-on") 297 .data("animating", "on") 298 .data("mousedown", "on"); 299 } else { 300 $ripple.animate({ 301 "width": Math.max($element.outerWidth(), $element.outerHeight()) * 2, 302 "height": Math.max($element.outerWidth(), $element.outerHeight()) * 2, 303 "margin-left": Math.max($element.outerWidth(), $element.outerHeight()) * (-1), 304 "margin-top": Math.max($element.outerWidth(), $element.outerHeight()) * (-1), 305 "opacity": 0.2 306 }, 500, function() { 307 $ripple.trigger("transitionend"); 308 }); 309 } 310 }; 311 312 313 /** 314 * Create the jquery plugin function 315 */ 316 $.fn.ripples = function(options) { 317 return this.each(function() { 318 if(!$.data(this, "plugin_" + ripples)) { 319 $.data(this, "plugin_" + ripples, new Ripples(this, options)); 320 } 321 }); 322 }; 323 324 })(jQuery, window, document);