ripple-effect.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. import { proxyCustomElement, HTMLElement, readTask, writeTask, h, Host } from '@stencil/core/internal/client';
  5. import { b as getIonMode } from './ionic-global.js';
  6. const rippleEffectCss = ":host{left:0;right:0;top:0;bottom:0;position:absolute;contain:strict;pointer-events:none}:host(.unbounded){contain:layout size style}.ripple-effect{border-radius:50%;position:absolute;background-color:currentColor;color:inherit;contain:strict;opacity:0;-webkit-animation:225ms rippleAnimation forwards, 75ms fadeInAnimation forwards;animation:225ms rippleAnimation forwards, 75ms fadeInAnimation forwards;will-change:transform, opacity;pointer-events:none}.fade-out{-webkit-transform:translate(var(--translate-end)) scale(var(--final-scale, 1));transform:translate(var(--translate-end)) scale(var(--final-scale, 1));-webkit-animation:150ms fadeOutAnimation forwards;animation:150ms fadeOutAnimation forwards}@-webkit-keyframes rippleAnimation{from{-webkit-animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:translate(var(--translate-end)) scale(var(--final-scale, 1));transform:translate(var(--translate-end)) scale(var(--final-scale, 1))}}@keyframes rippleAnimation{from{-webkit-animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:translate(var(--translate-end)) scale(var(--final-scale, 1));transform:translate(var(--translate-end)) scale(var(--final-scale, 1))}}@-webkit-keyframes fadeInAnimation{from{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0}to{opacity:0.16}}@keyframes fadeInAnimation{from{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0}to{opacity:0.16}}@-webkit-keyframes fadeOutAnimation{from{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0.16}to{opacity:0}}@keyframes fadeOutAnimation{from{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0.16}to{opacity:0}}";
  7. const IonRippleEffectStyle0 = rippleEffectCss;
  8. const RippleEffect = /*@__PURE__*/ proxyCustomElement(class RippleEffect extends HTMLElement {
  9. constructor() {
  10. super();
  11. this.__registerHost();
  12. this.__attachShadow();
  13. this.type = 'bounded';
  14. }
  15. /**
  16. * Adds the ripple effect to the parent element.
  17. *
  18. * @param x The horizontal coordinate of where the ripple should start.
  19. * @param y The vertical coordinate of where the ripple should start.
  20. */
  21. async addRipple(x, y) {
  22. return new Promise((resolve) => {
  23. readTask(() => {
  24. const rect = this.el.getBoundingClientRect();
  25. const width = rect.width;
  26. const height = rect.height;
  27. const hypotenuse = Math.sqrt(width * width + height * height);
  28. const maxDim = Math.max(height, width);
  29. const maxRadius = this.unbounded ? maxDim : hypotenuse + PADDING;
  30. const initialSize = Math.floor(maxDim * INITIAL_ORIGIN_SCALE);
  31. const finalScale = maxRadius / initialSize;
  32. let posX = x - rect.left;
  33. let posY = y - rect.top;
  34. if (this.unbounded) {
  35. posX = width * 0.5;
  36. posY = height * 0.5;
  37. }
  38. const styleX = posX - initialSize * 0.5;
  39. const styleY = posY - initialSize * 0.5;
  40. const moveX = width * 0.5 - posX;
  41. const moveY = height * 0.5 - posY;
  42. writeTask(() => {
  43. const div = document.createElement('div');
  44. div.classList.add('ripple-effect');
  45. const style = div.style;
  46. style.top = styleY + 'px';
  47. style.left = styleX + 'px';
  48. style.width = style.height = initialSize + 'px';
  49. style.setProperty('--final-scale', `${finalScale}`);
  50. style.setProperty('--translate-end', `${moveX}px, ${moveY}px`);
  51. const container = this.el.shadowRoot || this.el;
  52. container.appendChild(div);
  53. setTimeout(() => {
  54. resolve(() => {
  55. removeRipple(div);
  56. });
  57. }, 225 + 100);
  58. });
  59. });
  60. });
  61. }
  62. get unbounded() {
  63. return this.type === 'unbounded';
  64. }
  65. render() {
  66. const mode = getIonMode(this);
  67. return (h(Host, { key: '40c7f73e7f5f67e29f83e1236a61c6e1c9943c42', role: "presentation", class: {
  68. [mode]: true,
  69. unbounded: this.unbounded,
  70. } }));
  71. }
  72. get el() { return this; }
  73. static get style() { return IonRippleEffectStyle0; }
  74. }, [1, "ion-ripple-effect", {
  75. "type": [1],
  76. "addRipple": [64]
  77. }]);
  78. const removeRipple = (ripple) => {
  79. ripple.classList.add('fade-out');
  80. setTimeout(() => {
  81. ripple.remove();
  82. }, 200);
  83. };
  84. const PADDING = 10;
  85. const INITIAL_ORIGIN_SCALE = 0.5;
  86. function defineCustomElement() {
  87. if (typeof customElements === "undefined") {
  88. return;
  89. }
  90. const components = ["ion-ripple-effect"];
  91. components.forEach(tagName => { switch (tagName) {
  92. case "ion-ripple-effect":
  93. if (!customElements.get(tagName)) {
  94. customElements.define(tagName, RippleEffect);
  95. }
  96. break;
  97. } });
  98. }
  99. export { RippleEffect as R, defineCustomElement as d };