OLD | NEW |
1 <!-- | 1 <!-- |
2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | 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 | 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 | 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 | 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 | 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 | 7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
8 --> | 8 --> |
9 | 9 |
| 10 <link rel="import" href="../polymer/polymer.html"> |
| 11 <link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html
"> |
| 12 |
10 <!-- | 13 <!-- |
11 `paper-ripple` provides a visual effect that other paper elements can | 14 `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 | 15 use to simulate a rippling effect emanating from the point of contact. The |
13 effect can be visualized as a concentric circle with motion. | 16 effect can be visualized as a concentric circle with motion. |
14 | 17 |
15 Example: | 18 Example: |
16 | 19 |
17 <paper-ripple></paper-ripple> | 20 <paper-ripple></paper-ripple> |
18 | 21 |
19 `paper-ripple` listens to "mousedown" and "mouseup" events so it would display r
ipple | 22 `paper-ripple` listens to "mousedown" and "mouseup" events so it would display r
ipple |
20 effect when touches on it. You can also defeat the default behavior and | 23 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 | 24 manually route the down and up actions to the ripple element. Note that it is |
22 important if you call mousedownAction() you will have to make sure to call | 25 important if you call downAction() you will have to make sure to call |
23 mouseupAction() so that `paper-ripple` would end the animation loop. | 26 upAction() so that `paper-ripple` would end the animation loop. |
24 | 27 |
25 Example: | 28 Example: |
26 | 29 |
27 <paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple> | 30 <paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple> |
28 ... | 31 ... |
29 downAction: function(e) { | 32 downAction: function(e) { |
30 this.$.ripple.mousedownAction({x: e.x, y: e.y}); | 33 this.$.ripple.downAction({x: e.x, y: e.y}); |
31 }, | 34 }, |
32 upAction: function(e) { | 35 upAction: function(e) { |
33 this.$.ripple.mouseupAction(); | 36 this.$.ripple.upAction(); |
34 } | 37 } |
35 | 38 |
36 Styling ripple effect: | 39 Styling ripple effect: |
37 | 40 |
38 Use CSS color property to style the ripple: | 41 Use CSS color property to style the ripple: |
39 | 42 |
40 paper-ripple { | 43 paper-ripple { |
41 color: #4285f4; | 44 color: #4285f4; |
42 } | 45 } |
43 | 46 |
44 Note that CSS color property is inherited so it is not required to set it on | 47 Note that CSS color property is inherited so it is not required to set it on |
45 the `paper-ripple` element directly. | 48 the `paper-ripple` element directly. |
46 | 49 |
47 By default, the ripple is centered on the point of contact. Apply the `recenter
s` | 50 By default, the ripple is centered on the point of contact. Apply the `recenter
s` |
48 attribute to have the ripple grow toward the center of its container. | 51 attribute to have the ripple grow toward the center of its container. |
49 | 52 |
50 <paper-ripple recenters></paper-ripple> | 53 <paper-ripple recenters></paper-ripple> |
51 | 54 |
| 55 You can also center the ripple inside its container from the start. |
| 56 |
| 57 <paper-ripple center></paper-ripple> |
| 58 |
52 Apply `circle` class to make the rippling effect within a circle. | 59 Apply `circle` class to make the rippling effect within a circle. |
53 | 60 |
54 <paper-ripple class="circle"></paper-ripple> | 61 <paper-ripple class="circle"></paper-ripple> |
55 | 62 |
56 @group Paper Elements | 63 @group Paper Elements |
57 @element paper-ripple | 64 @element paper-ripple |
58 @homepage github.io | 65 @hero hero.svg |
| 66 @demo demo/index.html |
59 --> | 67 --> |
60 | 68 |
61 <!-- | 69 <dom-module id="paper-ripple"> |
62 Fired when the animation finishes. This is useful if you want to wait until the
ripple | |
63 animation finishes to perform some action. | |
64 | 70 |
65 @event transitionend | 71 <!-- |
66 @param {Object} detail | 72 Fired when the animation finishes. This is useful if you want to wait until th
e ripple |
67 @param {Object} detail.node The animated node | 73 animation finishes to perform some action. |
68 --> | |
69 | 74 |
70 <link rel="import" href="../polymer/polymer.html"> | 75 @event transitionend |
| 76 @param {Object} detail |
| 77 @param {Object} detail.node The animated node |
| 78 --> |
71 | 79 |
72 <dom-module id="paper-ripple"> | |
73 <style> | 80 <style> |
74 :host { | 81 :host { |
75 display: block; | 82 display: block; |
76 position: absolute; | 83 position: absolute; |
77 border-radius: inherit; | 84 border-radius: inherit; |
78 overflow: hidden; | 85 overflow: hidden; |
79 top: 0; | 86 top: 0; |
80 left: 0; | 87 left: 0; |
81 right: 0; | 88 right: 0; |
82 bottom: 0; | 89 bottom: 0; |
| 90 } |
83 | 91 |
84 /* This resolves a rendering issue in Chrome 40 where the | 92 :host([animating]) { |
| 93 /* This resolves a rendering issue in Chrome (as of 40) where the |
85 ripple is not properly clipped by its parent (which may have | 94 ripple is not properly clipped by its parent (which may have |
86 rounded corners. See: http://jsbin.com/temexa/4 */ | 95 rounded corners). See: http://jsbin.com/temexa/4 |
| 96 |
| 97 Note: We only apply this style conditionally. Otherwise, the browser |
| 98 will create a new compositing layer for every ripple element on the |
| 99 page, and that would be bad. */ |
87 -webkit-transform: translate(0, 0); | 100 -webkit-transform: translate(0, 0); |
88 transform: translate3d(0, 0, 0); | 101 transform: translate3d(0, 0, 0); |
89 } | 102 } |
90 | 103 |
91 :host([noink]) { | 104 :host([noink]) { |
92 pointer-events: none; | 105 pointer-events: none; |
93 } | 106 } |
94 | 107 |
95 #background, | 108 #background, |
96 #waves, | 109 #waves, |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
212 this.resetInteractionState(); | 225 this.resetInteractionState(); |
213 } | 226 } |
214 | 227 |
215 Ripple.MAX_RADIUS = 300; | 228 Ripple.MAX_RADIUS = 300; |
216 | 229 |
217 Ripple.prototype = { | 230 Ripple.prototype = { |
218 get recenters() { | 231 get recenters() { |
219 return this.element.recenters; | 232 return this.element.recenters; |
220 }, | 233 }, |
221 | 234 |
| 235 get center() { |
| 236 return this.element.center; |
| 237 }, |
| 238 |
222 get mouseDownElapsed() { | 239 get mouseDownElapsed() { |
223 var elapsed; | 240 var elapsed; |
224 | 241 |
225 if (!this.mouseDownStart) { | 242 if (!this.mouseDownStart) { |
226 return 0; | 243 return 0; |
227 } | 244 } |
228 | 245 |
229 elapsed = Utility.now() - this.mouseDownStart; | 246 elapsed = Utility.now() - this.mouseDownStart; |
230 | 247 |
231 if (this.mouseUpStart) { | 248 if (this.mouseUpStart) { |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
368 | 385 |
369 | 386 |
370 // 2d transform for safari because of border-radius and overflow:hidden
clipping bug. | 387 // 2d transform for safari because of border-radius and overflow:hidden
clipping bug. |
371 // https://bugs.webkit.org/show_bug.cgi?id=98538 | 388 // https://bugs.webkit.org/show_bug.cgi?id=98538 |
372 this.waveContainer.style.webkitTransform = 'translate(' + dx + 'px, ' +
dy + 'px)'; | 389 this.waveContainer.style.webkitTransform = 'translate(' + dx + 'px, ' +
dy + 'px)'; |
373 this.waveContainer.style.transform = 'translate3d(' + dx + 'px, ' + dy +
'px, 0)'; | 390 this.waveContainer.style.transform = 'translate3d(' + dx + 'px, ' + dy +
'px, 0)'; |
374 this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')'; | 391 this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')'; |
375 this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)'; | 392 this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)'; |
376 }, | 393 }, |
377 | 394 |
378 mousedownAction: function(event) { | 395 downAction: function(event) { |
| 396 var xCenter = this.containerMetrics.width / 2; |
| 397 var yCenter = this.containerMetrics.height / 2; |
| 398 |
379 this.resetInteractionState(); | 399 this.resetInteractionState(); |
380 this.mouseDownStart = Utility.now(); | 400 this.mouseDownStart = Utility.now(); |
381 | 401 |
382 this.xStart = event ? | 402 if (this.center) { |
383 event.x - this.containerMetrics.boundingRect.left : | 403 this.xStart = xCenter; |
384 this.containerMetrics.width / 2; | 404 this.yStart = yCenter; |
385 this.yStart = event ? | |
386 event.y - this.containerMetrics.boundingRect.top : | |
387 this.containerMetrics.height / 2; | |
388 | |
389 if (this.recenters) { | |
390 this.xEnd = this.containerMetrics.width / 2; | |
391 this.yEnd = this.containerMetrics.height / 2; | |
392 this.slideDistance = Utility.distance( | 405 this.slideDistance = Utility.distance( |
393 this.xStart, this.yStart, this.xEnd, this.yEnd | 406 this.xStart, this.yStart, this.xEnd, this.yEnd |
394 ); | 407 ); |
| 408 } else { |
| 409 this.xStart = event ? |
| 410 event.detail.x - this.containerMetrics.boundingRect.left : |
| 411 this.containerMetrics.width / 2; |
| 412 this.yStart = event ? |
| 413 event.detail.y - this.containerMetrics.boundingRect.top : |
| 414 this.containerMetrics.height / 2; |
| 415 } |
| 416 |
| 417 if (this.recenters) { |
| 418 this.xEnd = xCenter; |
| 419 this.yEnd = yCenter; |
| 420 this.slideDistance = Utility.distance( |
| 421 this.xStart, this.yStart, this.xEnd, this.yEnd |
| 422 ); |
395 } | 423 } |
396 | 424 |
397 this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom( | 425 this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom( |
398 this.xStart, | 426 this.xStart, |
399 this.yStart | 427 this.yStart |
400 ); | 428 ); |
401 | 429 |
402 this.waveContainer.style.top = | 430 this.waveContainer.style.top = |
403 (this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px'
; | 431 (this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px'
; |
404 this.waveContainer.style.left = | 432 this.waveContainer.style.left = |
405 (this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px'; | 433 (this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px'; |
406 | 434 |
407 this.waveContainer.style.width = this.containerMetrics.size + 'px'; | 435 this.waveContainer.style.width = this.containerMetrics.size + 'px'; |
408 this.waveContainer.style.height = this.containerMetrics.size + 'px'; | 436 this.waveContainer.style.height = this.containerMetrics.size + 'px'; |
409 }, | 437 }, |
410 | 438 |
411 mouseupAction: function(event) { | 439 upAction: function(event) { |
412 if (!this.isMouseDown) { | 440 if (!this.isMouseDown) { |
413 return; | 441 return; |
414 } | 442 } |
415 | 443 |
416 this.mouseUpStart = Utility.now(); | 444 this.mouseUpStart = Utility.now(); |
417 }, | 445 }, |
418 | 446 |
419 remove: function() { | 447 remove: function() { |
420 Polymer.dom(this.waveContainer.parentNode).removeChild( | 448 Polymer.dom(this.waveContainer.parentNode).removeChild( |
421 this.waveContainer | 449 this.waveContainer |
422 ); | 450 ); |
423 } | 451 } |
424 }; | 452 }; |
425 | 453 |
426 Polymer({ | 454 Polymer({ |
427 is: 'paper-ripple', | 455 is: 'paper-ripple', |
428 | 456 |
| 457 behaviors: [ |
| 458 Polymer.IronA11yKeysBehavior |
| 459 ], |
| 460 |
429 properties: { | 461 properties: { |
430 /** | 462 /** |
431 * The initial opacity set on the wave. | 463 * The initial opacity set on the wave. |
432 * | 464 * |
433 * @attribute initialOpacity | 465 * @attribute initialOpacity |
434 * @type number | 466 * @type number |
435 * @default 0.25 | 467 * @default 0.25 |
436 */ | 468 */ |
437 initialOpacity: { | 469 initialOpacity: { |
438 type: Number, | 470 type: Number, |
(...skipping 19 matching lines...) Expand all Loading... |
458 * @attribute recenters | 490 * @attribute recenters |
459 * @type boolean | 491 * @type boolean |
460 * @default false | 492 * @default false |
461 */ | 493 */ |
462 recenters: { | 494 recenters: { |
463 type: Boolean, | 495 type: Boolean, |
464 value: false | 496 value: false |
465 }, | 497 }, |
466 | 498 |
467 /** | 499 /** |
| 500 * If true, ripples will center inside its container |
| 501 * |
| 502 * @attribute recenters |
| 503 * @type boolean |
| 504 * @default false |
| 505 */ |
| 506 center: { |
| 507 type: Boolean, |
| 508 value: false |
| 509 }, |
| 510 |
| 511 /** |
468 * A list of the visual ripples. | 512 * A list of the visual ripples. |
469 * | 513 * |
470 * @attribute ripples | 514 * @attribute ripples |
471 * @type Array | 515 * @type Array |
472 * @default [] | 516 * @default [] |
473 */ | 517 */ |
474 ripples: { | 518 ripples: { |
475 type: Array, | 519 type: Array, |
476 value: function() { | 520 value: function() { |
477 return []; | 521 return []; |
478 } | 522 } |
479 }, | 523 }, |
480 | 524 |
| 525 /** |
| 526 * True when there are visible ripples animating within the |
| 527 * element. |
| 528 */ |
| 529 animating: { |
| 530 type: Boolean, |
| 531 readOnly: true, |
| 532 reflectToAttribute: true, |
| 533 value: false |
| 534 }, |
| 535 |
| 536 /** |
| 537 * If true, the ripple will remain in the "down" state until `holdDown` |
| 538 * is set to false again. |
| 539 */ |
| 540 holdDown: { |
| 541 type: Boolean, |
| 542 value: false, |
| 543 observer: '_holdDownChanged' |
| 544 }, |
| 545 |
481 _animating: { | 546 _animating: { |
482 type: Boolean | 547 type: Boolean |
483 }, | 548 }, |
484 | 549 |
485 _boundAnimate: { | 550 _boundAnimate: { |
486 type: Function, | 551 type: Function, |
487 value: function() { | 552 value: function() { |
488 return this.animate.bind(this); | 553 return this.animate.bind(this); |
489 } | 554 } |
490 }, | |
491 | |
492 _boundMousedownAction: { | |
493 type: Function, | |
494 value: function() { | |
495 return this.mousedownAction.bind(this); | |
496 } | |
497 }, | |
498 | |
499 _boundMouseupAction: { | |
500 type: Function, | |
501 value: function() { | |
502 return this.mouseupAction.bind(this); | |
503 } | |
504 } | 555 } |
505 }, | 556 }, |
506 | 557 |
507 get target () { | 558 get target () { |
508 return this.host || this.parentNode; | 559 var ownerRoot = Polymer.dom(this).getOwnerRoot(); |
| 560 var target; |
| 561 |
| 562 if (ownerRoot) { |
| 563 target = ownerRoot.host; |
| 564 } |
| 565 |
| 566 if (!target) { |
| 567 target = this.parentNode; |
| 568 } |
| 569 |
| 570 return target; |
| 571 }, |
| 572 |
| 573 keyBindings: { |
| 574 'enter:keydown': '_onEnterKeydown', |
| 575 'space:keydown': '_onSpaceKeydown', |
| 576 'space:keyup': '_onSpaceKeyup' |
509 }, | 577 }, |
510 | 578 |
511 attached: function() { | 579 attached: function() { |
512 this.target.addEventListener('mousedown', this._boundMousedownAction); | 580 this._listen(this.target, 'up', this.upAction.bind(this)); |
513 this.target.addEventListener('mouseup', this._boundMouseupAction); | 581 this._listen(this.target, 'down', this.downAction.bind(this)); |
| 582 |
| 583 if (!this.target.hasAttribute('noink')) { |
| 584 this.keyEventTarget = this.target; |
| 585 } |
514 }, | 586 }, |
515 | 587 |
516 detached: function() { | |
517 this.target.removeEventListener('mousedown', this._boundMousedownAction)
; | |
518 this.target.removeEventListener('mouseup', this._boundMouseupAction); | |
519 }, | |
520 | |
521 /* TODO(cdata): Replace the above attached / detached listeners when | |
522 PolymerGestures equivalent lands in 0.8. | |
523 listeners: { | |
524 mousedown: 'mousedownAction', | |
525 mouseup: 'mouseupAction' | |
526 }, | |
527 */ | |
528 | |
529 get shouldKeepAnimating () { | 588 get shouldKeepAnimating () { |
530 for (var index = 0; index < this.ripples.length; ++index) { | 589 for (var index = 0; index < this.ripples.length; ++index) { |
531 if (!this.ripples[index].isAnimationComplete) { | 590 if (!this.ripples[index].isAnimationComplete) { |
532 return true; | 591 return true; |
533 } | 592 } |
534 } | 593 } |
535 | 594 |
536 return false; | 595 return false; |
537 }, | 596 }, |
538 | 597 |
539 simulatedRipple: function() { | 598 simulatedRipple: function() { |
540 this.mousedownAction(null); | 599 this.downAction(null); |
541 | 600 |
542 // Please see polymer/polymer#1305 | 601 // Please see polymer/polymer#1305 |
543 this.async(function() { | 602 this.async(function() { |
544 this.mouseupAction(); | 603 this.upAction(); |
545 }, 1); | 604 }, 1); |
546 }, | 605 }, |
547 | 606 |
548 mousedownAction: function(event) { | 607 downAction: function(event) { |
| 608 if (this.holdDown && this.ripples.length > 0) { |
| 609 return; |
| 610 } |
| 611 |
549 var ripple = this.addRipple(); | 612 var ripple = this.addRipple(); |
550 | 613 |
551 ripple.mousedownAction(event); | 614 ripple.downAction(event); |
552 | 615 |
553 if (!this._animating) { | 616 if (!this._animating) { |
554 this.animate(); | 617 this.animate(); |
555 } | 618 } |
556 }, | 619 }, |
557 | 620 |
558 mouseupAction: function(event) { | 621 upAction: function(event) { |
| 622 if (this.holdDown) { |
| 623 return; |
| 624 } |
| 625 |
559 this.ripples.forEach(function(ripple) { | 626 this.ripples.forEach(function(ripple) { |
560 ripple.mouseupAction(event); | 627 ripple.upAction(event); |
561 }); | 628 }); |
562 | 629 |
563 this.animate(); | 630 this.animate(); |
564 }, | 631 }, |
565 | 632 |
566 onAnimationComplete: function() { | 633 onAnimationComplete: function() { |
567 this._animating = false; | 634 this._animating = false; |
568 this.$.background.style.backgroundColor = null; | 635 this.$.background.style.backgroundColor = null; |
569 this.fire('transitionend'); | 636 this.fire('transitionend'); |
570 }, | 637 }, |
571 | 638 |
572 addRipple: function() { | 639 addRipple: function() { |
573 var ripple = new Ripple(this); | 640 var ripple = new Ripple(this); |
574 | 641 |
575 Polymer.dom(this.$.waves).appendChild(ripple.waveContainer); | 642 Polymer.dom(this.$.waves).appendChild(ripple.waveContainer); |
576 this.$.background.style.backgroundColor = ripple.color; | 643 this.$.background.style.backgroundColor = ripple.color; |
577 this.ripples.push(ripple); | 644 this.ripples.push(ripple); |
578 | 645 |
| 646 this._setAnimating(true); |
| 647 |
579 return ripple; | 648 return ripple; |
580 }, | 649 }, |
581 | 650 |
582 removeRipple: function(ripple) { | 651 removeRipple: function(ripple) { |
583 var rippleIndex = this.ripples.indexOf(ripple); | 652 var rippleIndex = this.ripples.indexOf(ripple); |
584 | 653 |
585 if (rippleIndex < 0) { | 654 if (rippleIndex < 0) { |
586 return; | 655 return; |
587 } | 656 } |
588 | 657 |
589 this.ripples.splice(rippleIndex, 1); | 658 this.ripples.splice(rippleIndex, 1); |
590 | 659 |
591 ripple.remove(); | 660 ripple.remove(); |
| 661 |
| 662 if (!this.ripples.length) { |
| 663 this._setAnimating(false); |
| 664 } |
592 }, | 665 }, |
593 | 666 |
594 animate: function() { | 667 animate: function() { |
595 var index; | 668 var index; |
596 var ripple; | 669 var ripple; |
597 | 670 |
598 this._animating = true; | 671 this._animating = true; |
599 | 672 |
600 for (index = 0; index < this.ripples.length; ++index) { | 673 for (index = 0; index < this.ripples.length; ++index) { |
601 ripple = this.ripples[index]; | 674 ripple = this.ripples[index]; |
602 | 675 |
603 ripple.draw(); | 676 ripple.draw(); |
604 | 677 |
605 this.$.background.style.opacity = ripple.outerOpacity; | 678 this.$.background.style.opacity = ripple.outerOpacity; |
606 | 679 |
607 if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) { | 680 if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) { |
608 this.removeRipple(ripple); | 681 this.removeRipple(ripple); |
609 } | 682 } |
610 } | 683 } |
611 | 684 |
612 if (this.shouldKeepAnimating) { | 685 if (!this.shouldKeepAnimating && this.ripples.length === 0) { |
| 686 this.onAnimationComplete(); |
| 687 } else { |
613 window.requestAnimationFrame(this._boundAnimate); | 688 window.requestAnimationFrame(this._boundAnimate); |
614 } else if (this.ripples.length === 0) { | 689 } |
615 this.onAnimationComplete(); | 690 }, |
| 691 |
| 692 _onEnterKeydown: function() { |
| 693 this.downAction(); |
| 694 this.async(this.upAction, 1); |
| 695 }, |
| 696 |
| 697 _onSpaceKeydown: function() { |
| 698 this.downAction(); |
| 699 }, |
| 700 |
| 701 _onSpaceKeyup: function() { |
| 702 this.upAction(); |
| 703 }, |
| 704 |
| 705 _holdDownChanged: function(holdDown) { |
| 706 if (holdDown) { |
| 707 this.downAction(); |
| 708 } else { |
| 709 this.upAction(); |
616 } | 710 } |
617 } | 711 } |
618 }); | 712 }); |
619 })(); | 713 })(); |
620 </script> | 714 </script> |
OLD | NEW |