sync
[bootswatch] / material-design / js / ripples.js
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);