Skip to content

Commit dcc3bb1

Browse files
committed
fix: button wave not work
1 parent 7d1418d commit dcc3bb1

File tree

8 files changed

+352
-181
lines changed

8 files changed

+352
-181
lines changed

components/_util/wave.tsx

-178
This file was deleted.

components/_util/wave/WaveEffect.tsx

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

0 commit comments

Comments
 (0)