WaveEffect.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import { createVNode as _createVNode } from "vue";
  2. import { onBeforeUnmount, onMounted, Transition, render, defineComponent, shallowRef } from 'vue';
  3. import useState from '../hooks/useState';
  4. import { objectType } from '../type';
  5. import { getTargetWaveColor } from './util';
  6. import wrapperRaf from '../raf';
  7. function validateNum(value) {
  8. return Number.isNaN(value) ? 0 : value;
  9. }
  10. const WaveEffect = defineComponent({
  11. props: {
  12. target: objectType(),
  13. className: String
  14. },
  15. setup(props) {
  16. const divRef = shallowRef(null);
  17. const [color, setWaveColor] = useState(null);
  18. const [borderRadius, setBorderRadius] = useState([]);
  19. const [left, setLeft] = useState(0);
  20. const [top, setTop] = useState(0);
  21. const [width, setWidth] = useState(0);
  22. const [height, setHeight] = useState(0);
  23. const [enabled, setEnabled] = useState(false);
  24. function syncPos() {
  25. const {
  26. target
  27. } = props;
  28. const nodeStyle = getComputedStyle(target);
  29. // Get wave color from target
  30. setWaveColor(getTargetWaveColor(target));
  31. const isStatic = nodeStyle.position === 'static';
  32. // Rect
  33. const {
  34. borderLeftWidth,
  35. borderTopWidth
  36. } = nodeStyle;
  37. setLeft(isStatic ? target.offsetLeft : validateNum(-parseFloat(borderLeftWidth)));
  38. setTop(isStatic ? target.offsetTop : validateNum(-parseFloat(borderTopWidth)));
  39. setWidth(target.offsetWidth);
  40. setHeight(target.offsetHeight);
  41. // Get border radius
  42. const {
  43. borderTopLeftRadius,
  44. borderTopRightRadius,
  45. borderBottomLeftRadius,
  46. borderBottomRightRadius
  47. } = nodeStyle;
  48. setBorderRadius([borderTopLeftRadius, borderTopRightRadius, borderBottomRightRadius, borderBottomLeftRadius].map(radius => validateNum(parseFloat(radius))));
  49. }
  50. // Add resize observer to follow size
  51. let resizeObserver;
  52. let rafId;
  53. let timeoutId;
  54. const clear = () => {
  55. clearTimeout(timeoutId);
  56. wrapperRaf.cancel(rafId);
  57. resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect();
  58. };
  59. const removeDom = () => {
  60. var _a;
  61. const holder = (_a = divRef.value) === null || _a === void 0 ? void 0 : _a.parentElement;
  62. if (holder) {
  63. render(null, holder);
  64. if (holder.parentElement) {
  65. holder.parentElement.removeChild(holder);
  66. }
  67. }
  68. };
  69. onMounted(() => {
  70. clear();
  71. timeoutId = setTimeout(() => {
  72. removeDom();
  73. }, 5000);
  74. const {
  75. target
  76. } = props;
  77. if (target) {
  78. // We need delay to check position here
  79. // since UI may change after click
  80. rafId = wrapperRaf(() => {
  81. syncPos();
  82. setEnabled(true);
  83. });
  84. if (typeof ResizeObserver !== 'undefined') {
  85. resizeObserver = new ResizeObserver(syncPos);
  86. resizeObserver.observe(target);
  87. }
  88. }
  89. });
  90. onBeforeUnmount(() => {
  91. clear();
  92. });
  93. const onTransitionend = e => {
  94. if (e.propertyName === 'opacity') {
  95. removeDom();
  96. }
  97. };
  98. return () => {
  99. if (!enabled.value) {
  100. return null;
  101. }
  102. const waveStyle = {
  103. left: `${left.value}px`,
  104. top: `${top.value}px`,
  105. width: `${width.value}px`,
  106. height: `${height.value}px`,
  107. borderRadius: borderRadius.value.map(radius => `${radius}px`).join(' ')
  108. };
  109. if (color) {
  110. waveStyle['--wave-color'] = color.value;
  111. }
  112. return _createVNode(Transition, {
  113. "appear": true,
  114. "name": "wave-motion",
  115. "appearFromClass": "wave-motion-appear",
  116. "appearActiveClass": "wave-motion-appear",
  117. "appearToClass": "wave-motion-appear wave-motion-appear-active"
  118. }, {
  119. default: () => [_createVNode("div", {
  120. "ref": divRef,
  121. "class": props.className,
  122. "style": waveStyle,
  123. "onTransitionend": onTransitionend
  124. }, null)]
  125. });
  126. };
  127. }
  128. });
  129. function showWaveEffect(node, className) {
  130. // Create holder
  131. const holder = document.createElement('div');
  132. holder.style.position = 'absolute';
  133. holder.style.left = `0px`;
  134. holder.style.top = `0px`;
  135. node === null || node === void 0 ? void 0 : node.insertBefore(holder, node === null || node === void 0 ? void 0 : node.firstChild);
  136. render(_createVNode(WaveEffect, {
  137. "target": node,
  138. "className": className
  139. }, null), holder);
  140. }
  141. export default showWaveEffect;