Port Android relocation packer to chromium build
[chromium-blink-merge.git] / third_party / polymer / components-chromium / paper-ripple / paper-ripple-extracted.js
blob6a6469ef363c4a07b6af0d916629bc5609c3975d
3   (function() {
5     var waveMaxRadius = 150;
6     //
7     // INK EQUATIONS
8     //
9     function waveRadiusFn(touchDownMs, touchUpMs, anim) {
10       // Convert from ms to s
11       var touchDown = touchDownMs / 1000;
12       var touchUp = touchUpMs / 1000;
13       var totalElapsed = touchDown + touchUp;
14       var ww = anim.width, hh = anim.height;
15       // use diagonal size of container to avoid floating point math sadness
16       var waveRadius = Math.min(Math.sqrt(ww * ww + hh * hh), waveMaxRadius) * 1.1 + 5;
17       var duration = 1.1 - .2 * (waveRadius / waveMaxRadius);
18       var tt = (totalElapsed / duration);
20       var size = waveRadius * (1 - Math.pow(80, -tt));
21       return Math.abs(size);
22     }
24     function waveOpacityFn(td, tu, anim) {
25       // Convert from ms to s.
26       var touchDown = td / 1000;
27       var touchUp = tu / 1000;
28       var totalElapsed = touchDown + touchUp;
30       if (tu <= 0) {  // before touch up
31         return anim.initialOpacity;
32       }
33       return Math.max(0, anim.initialOpacity - touchUp * anim.opacityDecayVelocity);
34     }
36     function waveOuterOpacityFn(td, tu, anim) {
37       // Convert from ms to s.
38       var touchDown = td / 1000;
39       var touchUp = tu / 1000;
41       // Linear increase in background opacity, capped at the opacity
42       // of the wavefront (waveOpacity).
43       var outerOpacity = touchDown * 0.3;
44       var waveOpacity = waveOpacityFn(td, tu, anim);
45       return Math.max(0, Math.min(outerOpacity, waveOpacity));
46     }
48     // Determines whether the wave should be completely removed.
49     function waveDidFinish(wave, radius, anim) {
50       var waveOpacity = waveOpacityFn(wave.tDown, wave.tUp, anim);
52       // If the wave opacity is 0 and the radius exceeds the bounds
53       // of the element, then this is finished.
54       return waveOpacity < 0.01 && radius >= Math.min(wave.maxRadius, waveMaxRadius);
55     };
57     function waveAtMaximum(wave, radius, anim) {
58       var waveOpacity = waveOpacityFn(wave.tDown, wave.tUp, anim);
60       return waveOpacity >= anim.initialOpacity && radius >= Math.min(wave.maxRadius, waveMaxRadius);
61     }
63     //
64     // DRAWING
65     //
66     function drawRipple(ctx, x, y, radius, innerAlpha, outerAlpha) {
67       // Only animate opacity and transform
68       if (outerAlpha !== undefined) {
69         ctx.bg.style.opacity = outerAlpha;
70       }
71       ctx.wave.style.opacity = innerAlpha;
73       var s = radius / (ctx.containerSize / 2);
74       var dx = x - (ctx.containerWidth / 2);
75       var dy = y - (ctx.containerHeight / 2);
77       ctx.wc.style.webkitTransform = 'translate3d(' + dx + 'px,' + dy + 'px,0)';
78       ctx.wc.style.transform = 'translate3d(' + dx + 'px,' + dy + 'px,0)';
80       // 2d transform for safari because of border-radius and overflow:hidden clipping bug.
81       // https://bugs.webkit.org/show_bug.cgi?id=98538
82       ctx.wave.style.webkitTransform = 'scale(' + s + ',' + s + ')';
83       ctx.wave.style.transform = 'scale3d(' + s + ',' + s + ',1)';
84     }
86     //
87     // SETUP
88     //
89     function createWave(elem) {
90       var elementStyle = window.getComputedStyle(elem);
91       var fgColor = elementStyle.color;
93       var inner = document.createElement('div');
94       inner.style.backgroundColor = fgColor;
95       inner.classList.add('wave');
97       var outer = document.createElement('div');
98       outer.classList.add('wave-container');
99       outer.appendChild(inner);
101       var container = elem.$.waves;
102       container.appendChild(outer);
104       elem.$.bg.style.backgroundColor = fgColor;
106       var wave = {
107         bg: elem.$.bg,
108         wc: outer,
109         wave: inner,
110         waveColor: fgColor,
111         maxRadius: 0,
112         isMouseDown: false,
113         mouseDownStart: 0.0,
114         mouseUpStart: 0.0,
115         tDown: 0,
116         tUp: 0
117       };
118       return wave;
119     }
121     function removeWaveFromScope(scope, wave) {
122       if (scope.waves) {
123         var pos = scope.waves.indexOf(wave);
124         scope.waves.splice(pos, 1);
125         // FIXME cache nodes
126         wave.wc.remove();
127       }
128     };
130     // Shortcuts.
131     var pow = Math.pow;
132     var now = Date.now;
133     if (window.performance && performance.now) {
134       now = performance.now.bind(performance);
135     }
137     function cssColorWithAlpha(cssColor, alpha) {
138         var parts = cssColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
139         if (typeof alpha == 'undefined') {
140             alpha = 1;
141         }
142         if (!parts) {
143           return 'rgba(255, 255, 255, ' + alpha + ')';
144         }
145         return 'rgba(' + parts[1] + ', ' + parts[2] + ', ' + parts[3] + ', ' + alpha + ')';
146     }
148     function dist(p1, p2) {
149       return Math.sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
150     }
152     function distanceFromPointToFurthestCorner(point, size) {
153       var tl_d = dist(point, {x: 0, y: 0});
154       var tr_d = dist(point, {x: size.w, y: 0});
155       var bl_d = dist(point, {x: 0, y: size.h});
156       var br_d = dist(point, {x: size.w, y: size.h});
157       return Math.max(tl_d, tr_d, bl_d, br_d);
158     }
160     Polymer('paper-ripple', {
162       /**
163        * The initial opacity set on the wave.
164        *
165        * @attribute initialOpacity
166        * @type number
167        * @default 0.25
168        */
169       initialOpacity: 0.25,
171       /**
172        * How fast (opacity per second) the wave fades out.
173        *
174        * @attribute opacityDecayVelocity
175        * @type number
176        * @default 0.8
177        */
178       opacityDecayVelocity: 0.8,
180       backgroundFill: true,
181       pixelDensity: 2,
183       eventDelegates: {
184         down: 'downAction',
185         up: 'upAction'
186       },
188       ready: function() {
189         this.waves = [];
190       },
192       downAction: function(e) {
193         var wave = createWave(this);
195         this.cancelled = false;
196         wave.isMouseDown = true;
197         wave.tDown = 0.0;
198         wave.tUp = 0.0;
199         wave.mouseUpStart = 0.0;
200         wave.mouseDownStart = now();
202         var rect = this.getBoundingClientRect();
203         var width = rect.width;
204         var height = rect.height;
205         var touchX = e.x - rect.left;
206         var touchY = e.y - rect.top;
208         wave.startPosition = {x:touchX, y:touchY};
210         if (this.classList.contains("recenteringTouch")) {
211           wave.endPosition = {x: width / 2,  y: height / 2};
212           wave.slideDistance = dist(wave.startPosition, wave.endPosition);
213         }
214         wave.containerSize = Math.max(width, height);
215         wave.containerWidth = width;
216         wave.containerHeight = height;
217         wave.maxRadius = distanceFromPointToFurthestCorner(wave.startPosition, {w: width, h: height});
219         // The wave is circular so constrain its container to 1:1
220         wave.wc.style.top = (wave.containerHeight - wave.containerSize) / 2 + 'px';
221         wave.wc.style.left = (wave.containerWidth - wave.containerSize) / 2 + 'px';
222         wave.wc.style.width = wave.containerSize + 'px';
223         wave.wc.style.height = wave.containerSize + 'px';
225         this.waves.push(wave);
227         if (!this._loop) {
228           this._loop = this.animate.bind(this, {
229             width: width,
230             height: height
231           });
232           requestAnimationFrame(this._loop);
233         }
234         // else there is already a rAF
235       },
237       upAction: function() {
238         for (var i = 0; i < this.waves.length; i++) {
239           // Declare the next wave that has mouse down to be mouse'ed up.
240           var wave = this.waves[i];
241           if (wave.isMouseDown) {
242             wave.isMouseDown = false
243             wave.mouseUpStart = now();
244             wave.mouseDownStart = 0;
245             wave.tUp = 0.0;
246             break;
247           }
248         }
249         this._loop && requestAnimationFrame(this._loop);
250       },
252       cancel: function() {
253         this.cancelled = true;
254       },
256       animate: function(ctx) {
257         var shouldRenderNextFrame = false;
259         var deleteTheseWaves = [];
260         // The oldest wave's touch down duration
261         var longestTouchDownDuration = 0;
262         var longestTouchUpDuration = 0;
263         // Save the last known wave color
264         var lastWaveColor = null;
265         // wave animation values
266         var anim = {
267           initialOpacity: this.initialOpacity,
268           opacityDecayVelocity: this.opacityDecayVelocity,
269           height: ctx.height,
270           width: ctx.width
271         }
273         for (var i = 0; i < this.waves.length; i++) {
274           var wave = this.waves[i];
276           if (wave.mouseDownStart > 0) {
277             wave.tDown = now() - wave.mouseDownStart;
278           }
279           if (wave.mouseUpStart > 0) {
280             wave.tUp = now() - wave.mouseUpStart;
281           }
283           // Determine how long the touch has been up or down.
284           var tUp = wave.tUp;
285           var tDown = wave.tDown;
286           longestTouchDownDuration = Math.max(longestTouchDownDuration, tDown);
287           longestTouchUpDuration = Math.max(longestTouchUpDuration, tUp);
289           // Obtain the instantenous size and alpha of the ripple.
290           var radius = waveRadiusFn(tDown, tUp, anim);
291           var waveAlpha =  waveOpacityFn(tDown, tUp, anim);
292           var waveColor = cssColorWithAlpha(wave.waveColor, waveAlpha);
293           lastWaveColor = wave.waveColor;
295           // Position of the ripple.
296           var x = wave.startPosition.x;
297           var y = wave.startPosition.y;
299           // Ripple gravitational pull to the center of the canvas.
300           if (wave.endPosition) {
302             // This translates from the origin to the center of the view  based on the max dimension of
303             var translateFraction = Math.min(1, radius / wave.containerSize * 2 / Math.sqrt(2) );
305             x += translateFraction * (wave.endPosition.x - wave.startPosition.x);
306             y += translateFraction * (wave.endPosition.y - wave.startPosition.y);
307           }
309           // If we do a background fill fade too, work out the correct color.
310           var bgFillColor = null;
311           if (this.backgroundFill) {
312             var bgFillAlpha = waveOuterOpacityFn(tDown, tUp, anim);
313             bgFillColor = cssColorWithAlpha(wave.waveColor, bgFillAlpha);
314           }
316           // Draw the ripple.
317           drawRipple(wave, x, y, radius, waveAlpha, bgFillAlpha);
319           // Determine whether there is any more rendering to be done.
320           var maximumWave = waveAtMaximum(wave, radius, anim);
321           var waveDissipated = waveDidFinish(wave, radius, anim);
322           var shouldKeepWave = !waveDissipated || maximumWave;
323           // keep rendering dissipating wave when at maximum radius on upAction
324           var shouldRenderWaveAgain = wave.mouseUpStart ? !waveDissipated : !maximumWave;
325           shouldRenderNextFrame = shouldRenderNextFrame || shouldRenderWaveAgain;
326           if (!shouldKeepWave || this.cancelled) {
327             deleteTheseWaves.push(wave);
328           }
329        }
331         if (shouldRenderNextFrame) {
332           requestAnimationFrame(this._loop);
333         }
335         for (var i = 0; i < deleteTheseWaves.length; ++i) {
336           var wave = deleteTheseWaves[i];
337           removeWaveFromScope(this, wave);
338         }
340         if (!this.waves.length && this._loop) {
341           // clear the background color
342           this.$.bg.style.backgroundColor = null;
343           this._loop = null;
344           this.fire('core-transitionend');
345         }
346       }
348     });
350   })();