Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(686)

Side by Side Diff: third_party/polymer/components/paper-ripple/paper-ripple.html

Issue 582873003: Polymer elements added to third_party/polymer. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!--
2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
5 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
6 Code distributed by Google as part of the polymer project is also
7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
8 -->
9
10 <!--
11 `paper-ripple` provides a visual effect that other paper elements can
12 use to simulate a rippling effect emanating from the point of contact. The
13 effect can be visualized as a concentric circle with motion.
14
15 Example:
16
17 <paper-ripple></paper-ripple>
18
19 `paper-ripple` listens to "down" and "up" events so it would display ripple
20 effect when touches on it. You can also defeat the default behavior and
21 manually route the down and up actions to the ripple element. Note that it is
22 important if you call downAction() you will have to make sure to call upAction()
23 so that `paper-ripple` would end the animation loop.
24
25 Example:
26
27 <paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple>
28 ...
29 downAction: function(e) {
30 this.$.ripple.downAction({x: e.x, y: e.y});
31 },
32 upAction: function(e) {
33 this.$.ripple.upAction();
34 }
35
36 Styling ripple effect:
37
38 Use CSS color property to style the ripple:
39
40 paper-ripple {
41 color: #4285f4;
42 }
43
44 Note that CSS color property is inherited so it is not required to set it on
45 the `paper-ripple` element directly.
46
47 Apply `recenteringTouch` class to make the recentering rippling effect.
48
49 <paper-ripple class="recenteringTouch"></paper-ripple>
50
51 Apply `circle` class to make the rippling effect within a circle.
52
53 <paper-ripple class="circle"></paper-ripple>
54
55 @group Paper Elements
56 @element paper-ripple
57 @homepage github.io
58 -->
59
60 <link rel="import" href="../polymer/polymer.html" >
61
62 <polymer-element name="paper-ripple" attributes="initialOpacity opacityDecayVelo city">
63 <template>
64
65 <style>
66
67 :host {
68 display: block;
69 position: relative;
70 }
71
72 #canvas {
73 pointer-events: none;
74 position: absolute;
75 top: 0;
76 left: 0;
77 width: 100%;
78 height: 100%;
79 }
80
81 :host(.circle) #canvas {
82 border-radius: 50%;
83 }
84
85 </style>
86
87 </template>
88 <script>
89
90 (function() {
91
92 var waveMaxRadius = 150;
93 //
94 // INK EQUATIONS
95 //
96 function waveRadiusFn(touchDownMs, touchUpMs, anim) {
97 // Convert from ms to s.
98 var touchDown = touchDownMs / 1000;
99 var touchUp = touchUpMs / 1000;
100 var totalElapsed = touchDown + touchUp;
101 var ww = anim.width, hh = anim.height;
102 // use diagonal size of container to avoid floating point math sadness
103 var waveRadius = Math.min(Math.sqrt(ww * ww + hh * hh), waveMaxRadius) * 1 .1 + 5;
104 var duration = 1.1 - .2 * (waveRadius / waveMaxRadius);
105 var tt = (totalElapsed / duration);
106
107 var size = waveRadius * (1 - Math.pow(80, -tt));
108 return Math.abs(size);
109 }
110
111 function waveOpacityFn(td, tu, anim) {
112 // Convert from ms to s.
113 var touchDown = td / 1000;
114 var touchUp = tu / 1000;
115 var totalElapsed = touchDown + touchUp;
116
117 if (tu <= 0) { // before touch up
118 return anim.initialOpacity;
119 }
120 return Math.max(0, anim.initialOpacity - touchUp * anim.opacityDecayVeloci ty);
121 }
122
123 function waveOuterOpacityFn(td, tu, anim) {
124 // Convert from ms to s.
125 var touchDown = td / 1000;
126 var touchUp = tu / 1000;
127
128 // Linear increase in background opacity, capped at the opacity
129 // of the wavefront (waveOpacity).
130 var outerOpacity = touchDown * 0.3;
131 var waveOpacity = waveOpacityFn(td, tu, anim);
132 return Math.max(0, Math.min(outerOpacity, waveOpacity));
133 }
134
135 // Determines whether the wave should be completely removed.
136 function waveDidFinish(wave, radius, anim) {
137 var waveOpacity = waveOpacityFn(wave.tDown, wave.tUp, anim);
138 // If the wave opacity is 0 and the radius exceeds the bounds
139 // of the element, then this is finished.
140 if (waveOpacity < 0.01 && radius >= Math.min(wave.maxRadius, waveMaxRadius )) {
141 return true;
142 }
143 return false;
144 };
145
146 function waveAtMaximum(wave, radius, anim) {
147 var waveOpacity = waveOpacityFn(wave.tDown, wave.tUp, anim);
148 if (waveOpacity >= anim.initialOpacity && radius >= Math.min(wave.maxRadiu s, waveMaxRadius)) {
149 return true;
150 }
151 return false;
152 }
153
154 //
155 // DRAWING
156 //
157 function drawRipple(ctx, x, y, radius, innerColor, outerColor) {
158 if (outerColor) {
159 ctx.fillStyle = outerColor;
160 ctx.fillRect(0,0,ctx.canvas.width, ctx.canvas.height);
161 }
162 ctx.beginPath();
163 ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
164 ctx.fillStyle = innerColor;
165 ctx.fill();
166 }
167
168 //
169 // SETUP
170 //
171 function createWave(elem) {
172 var elementStyle = window.getComputedStyle(elem);
173 var fgColor = elementStyle.color;
174
175 var wave = {
176 waveColor: fgColor,
177 maxRadius: 0,
178 isMouseDown: false,
179 mouseDownStart: 0.0,
180 mouseUpStart: 0.0,
181 tDown: 0,
182 tUp: 0
183 };
184 return wave;
185 }
186
187 function removeWaveFromScope(scope, wave) {
188 if (scope.waves) {
189 var pos = scope.waves.indexOf(wave);
190 scope.waves.splice(pos, 1);
191 }
192 };
193
194 // Shortcuts.
195 var pow = Math.pow;
196 var now = Date.now;
197 if (window.performance && performance.now) {
198 now = performance.now.bind(performance);
199 }
200
201 function cssColorWithAlpha(cssColor, alpha) {
202 var parts = cssColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
203 if (typeof alpha == 'undefined') {
204 alpha = 1;
205 }
206 if (!parts) {
207 return 'rgba(255, 255, 255, ' + alpha + ')';
208 }
209 return 'rgba(' + parts[1] + ', ' + parts[2] + ', ' + parts[3] + ', ' + a lpha + ')';
210 }
211
212 function dist(p1, p2) {
213 return Math.sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
214 }
215
216 function distanceFromPointToFurthestCorner(point, size) {
217 var tl_d = dist(point, {x: 0, y: 0});
218 var tr_d = dist(point, {x: size.w, y: 0});
219 var bl_d = dist(point, {x: 0, y: size.h});
220 var br_d = dist(point, {x: size.w, y: size.h});
221 return Math.max(tl_d, tr_d, bl_d, br_d);
222 }
223
224 Polymer('paper-ripple', {
225
226 /**
227 * The initial opacity set on the wave.
228 *
229 * @attribute initialOpacity
230 * @type number
231 * @default 0.25
232 */
233 initialOpacity: 0.25,
234
235 /**
236 * How fast (opacity per second) the wave fades out.
237 *
238 * @attribute opacityDecayVelocity
239 * @type number
240 * @default 0.8
241 */
242 opacityDecayVelocity: 0.8,
243
244 backgroundFill: true,
245 pixelDensity: 2,
246
247 eventDelegates: {
248 down: 'downAction',
249 up: 'upAction'
250 },
251
252 attached: function() {
253 // create the canvas element manually becase ios
254 // does not render the canvas element if it is not created in the
255 // main document (component templates are created in a
256 // different document). See:
257 // https://bugs.webkit.org/show_bug.cgi?id=109073.
258 if (!this.$.canvas) {
259 var canvas = document.createElement('canvas');
260 canvas.id = 'canvas';
261 this.shadowRoot.appendChild(canvas);
262 this.$.canvas = canvas;
263 }
264 },
265
266 ready: function() {
267 this.waves = [];
268 },
269
270 setupCanvas: function() {
271 this.$.canvas.setAttribute('width', this.$.canvas.clientWidth * this.pix elDensity + "px");
272 this.$.canvas.setAttribute('height', this.$.canvas.clientHeight * this.p ixelDensity + "px");
273 var ctx = this.$.canvas.getContext('2d');
274 ctx.scale(this.pixelDensity, this.pixelDensity);
275 if (!this._loop) {
276 this._loop = this.animate.bind(this, ctx);
277 }
278 },
279
280 downAction: function(e) {
281 this.setupCanvas();
282 var wave = createWave(this.$.canvas);
283
284 this.cancelled = false;
285 wave.isMouseDown = true;
286 wave.tDown = 0.0;
287 wave.tUp = 0.0;
288 wave.mouseUpStart = 0.0;
289 wave.mouseDownStart = now();
290
291 var width = this.$.canvas.width / 2; // Retina canvas
292 var height = this.$.canvas.height / 2;
293 var rect = this.getBoundingClientRect();
294 var touchX = e.x - rect.left;
295 var touchY = e.y - rect.top;
296
297 wave.startPosition = {x:touchX, y:touchY};
298
299 if (this.classList.contains("recenteringTouch")) {
300 wave.endPosition = {x: width / 2, y: height / 2};
301 wave.slideDistance = dist(wave.startPosition, wave.endPosition);
302 }
303 wave.containerSize = Math.max(width, height);
304 wave.maxRadius = distanceFromPointToFurthestCorner(wave.startPosition, { w: width, h: height});
305 this.waves.push(wave);
306 requestAnimationFrame(this._loop);
307 },
308
309 upAction: function() {
310 for (var i = 0; i < this.waves.length; i++) {
311 // Declare the next wave that has mouse down to be mouse'ed up.
312 var wave = this.waves[i];
313 if (wave.isMouseDown) {
314 wave.isMouseDown = false
315 wave.mouseUpStart = now();
316 wave.mouseDownStart = 0;
317 wave.tUp = 0.0;
318 break;
319 }
320 }
321 this._loop && requestAnimationFrame(this._loop);
322 },
323
324 cancel: function() {
325 this.cancelled = true;
326 },
327
328 animate: function(ctx) {
329 var shouldRenderNextFrame = false;
330
331 // Clear the canvas
332 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
333
334 var deleteTheseWaves = [];
335 // The oldest wave's touch down duration
336 var longestTouchDownDuration = 0;
337 var longestTouchUpDuration = 0;
338 // Save the last known wave color
339 var lastWaveColor = null;
340 // wave animation values
341 var anim = {
342 initialOpacity: this.initialOpacity,
343 opacityDecayVelocity: this.opacityDecayVelocity,
344 height: ctx.canvas.height,
345 width: ctx.canvas.width
346 }
347
348 for (var i = 0; i < this.waves.length; i++) {
349 var wave = this.waves[i];
350
351 if (wave.mouseDownStart > 0) {
352 wave.tDown = now() - wave.mouseDownStart;
353 }
354 if (wave.mouseUpStart > 0) {
355 wave.tUp = now() - wave.mouseUpStart;
356 }
357
358 // Determine how long the touch has been up or down.
359 var tUp = wave.tUp;
360 var tDown = wave.tDown;
361 longestTouchDownDuration = Math.max(longestTouchDownDuration, tDown);
362 longestTouchUpDuration = Math.max(longestTouchUpDuration, tUp);
363
364 // Obtain the instantenous size and alpha of the ripple.
365 var radius = waveRadiusFn(tDown, tUp, anim);
366 var waveAlpha = waveOpacityFn(tDown, tUp, anim);
367 var waveColor = cssColorWithAlpha(wave.waveColor, waveAlpha);
368 lastWaveColor = wave.waveColor;
369
370 // Position of the ripple.
371 var x = wave.startPosition.x;
372 var y = wave.startPosition.y;
373
374 // Ripple gravitational pull to the center of the canvas.
375 if (wave.endPosition) {
376
377 // This translates from the origin to the center of the view based on the max dimension of
378 var translateFraction = Math.min(1, radius / wave.containerSize * 2 / Math.sqrt(2) );
379
380 x += translateFraction * (wave.endPosition.x - wave.startPosition.x) ;
381 y += translateFraction * (wave.endPosition.y - wave.startPosition.y) ;
382 }
383
384 // If we do a background fill fade too, work out the correct color.
385 var bgFillColor = null;
386 if (this.backgroundFill) {
387 var bgFillAlpha = waveOuterOpacityFn(tDown, tUp, anim);
388 bgFillColor = cssColorWithAlpha(wave.waveColor, bgFillAlpha);
389 }
390
391 // Draw the ripple.
392 drawRipple(ctx, x, y, radius, waveColor, bgFillColor);
393
394 // Determine whether there is any more rendering to be done.
395 var maximumWave = waveAtMaximum(wave, radius, anim);
396 var waveDissipated = waveDidFinish(wave, radius, anim);
397 var shouldKeepWave = !waveDissipated || maximumWave;
398 var shouldRenderWaveAgain = !waveDissipated && !maximumWave;
399 shouldRenderNextFrame = shouldRenderNextFrame || shouldRenderWaveAgain ;
400 if (!shouldKeepWave || this.cancelled) {
401 deleteTheseWaves.push(wave);
402 }
403 }
404
405 if (shouldRenderNextFrame) {
406 requestAnimationFrame(this._loop);
407 }
408
409 for (var i = 0; i < deleteTheseWaves.length; ++i) {
410 var wave = deleteTheseWaves[i];
411 removeWaveFromScope(this, wave);
412 }
413
414 if (!this.waves.length) {
415 // If there is nothing to draw, clear any drawn waves now because
416 // we're not going to get another requestAnimationFrame any more.
417 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
418 this._loop = null;
419 }
420 }
421
422 });
423
424 })();
425
426 </script>
427 </polymer-element>
OLDNEW
« no previous file with comments | « third_party/polymer/components/paper-ripple/metadata.html ('k') | third_party/polymer/components/paper-ripple/raw.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698