Skip to content

Commit b8af50b

Browse files
committed
新增萤火虫特效按钮
1 parent 550be51 commit b8af50b

File tree

4 files changed

+453
-124
lines changed

4 files changed

+453
-124
lines changed

src/hooks/useCursorMove.ts

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/**
2+
* 实现圆点延迟跟随鼠标移动效果
3+
*/
4+
import { ref, Ref, watch, onUnmounted } from 'vue';
5+
import { useGlobalContext } from 'hooks/useGlobalContext';
6+
import type { GlobalContext } from 'hooks/useGlobalContext';
7+
8+
interface MousePos {
9+
mouseX: number | null;
10+
mouseY: number | null;
11+
}
12+
type CursorSize = 'default' | 'big';
13+
14+
const useCursorMove = (
15+
containerRef: Ref<HTMLDivElement | null>,
16+
cursorRef: Ref<HTMLDivElement | null>,
17+
cursorDefaultRadius = 10,
18+
cursorBigRadius = 40,
19+
cursorMoveVCoefficient = 0.12
20+
) => {
21+
const globalContext = useGlobalContext() as Ref<GlobalContext>;
22+
const showCursor = ref<boolean>(false);
23+
const cursorSize = ref<CursorSize>('default');
24+
const mousePos = ref<MousePos>({
25+
mouseX: null,
26+
mouseY: null,
27+
});
28+
const cursorFrameId = ref<number>(0);
29+
30+
const onMouseEnter = (e: MouseEvent) => {
31+
const containerNode = containerRef.value;
32+
const cursorNode = cursorRef.value;
33+
if (!containerNode || !cursorNode) return;
34+
const { clientX, clientY } = e;
35+
showCursor.value = true;
36+
const { offsetLeft = 0, offsetTop = 0 } = containerNode;
37+
mousePos.value = {
38+
mouseX: clientX - offsetLeft,
39+
// @ts-ignore
40+
mouseY: clientY - (offsetTop - globalContext.value.scrollTop),
41+
};
42+
43+
cursorNode.style.left = `${
44+
(mousePos.value.mouseX || 0) - cursorDefaultRadius
45+
}px`;
46+
cursorNode.style.top = `${
47+
(mousePos.value.mouseY || 0) - cursorDefaultRadius
48+
}px`;
49+
};
50+
51+
const onMouseMove = (e: MouseEvent) => {
52+
const containerNode = containerRef.value;
53+
if (!containerNode) return;
54+
const { clientX, clientY } = e;
55+
const { offsetLeft = 0, offsetTop = 0 } = containerNode;
56+
mousePos.value = {
57+
mouseX: clientX - offsetLeft,
58+
// @ts-ignore
59+
mouseY: clientY - (offsetTop - globalContext.value.scrollTop),
60+
};
61+
};
62+
63+
const onMouseLeave = (e: MouseEvent) => {
64+
const containerNode = containerRef.value;
65+
if (!containerNode) return;
66+
const { clientX, clientY } = e;
67+
showCursor.value = false;
68+
if (!containerNode) return;
69+
const { offsetLeft = 0, offsetTop = 0 } = containerNode;
70+
mousePos.value = {
71+
mouseX: clientX - offsetLeft,
72+
// @ts-ignore
73+
mouseY: clientY - (offsetTop - globalContext.value.scrollTop),
74+
};
75+
};
76+
77+
const loop = () => {
78+
const { mouseX, mouseY } = mousePos.value;
79+
if (mouseX !== null && mouseY !== null) {
80+
let cursorRadius = cursorDefaultRadius;
81+
if (cursorSize.value === 'big') {
82+
cursorRadius = cursorBigRadius;
83+
}
84+
const cursorNode = cursorRef.value;
85+
if (!cursorNode) return;
86+
87+
const left = cursorNode.style.left
88+
? parseFloat(cursorNode.style.left)
89+
: null;
90+
const top = cursorNode.style.top
91+
? parseFloat(cursorNode.style.top)
92+
: null;
93+
if (left !== null && top !== null) {
94+
const distX = mouseX - (left + cursorRadius);
95+
const distY = mouseY - (top + cursorRadius);
96+
const dist = Math.hypot(distX, distY);
97+
if (dist > 0) {
98+
if (dist <= 0.1) {
99+
cursorNode.style.left = `${left}px`;
100+
cursorNode.style.top = `${top}px`;
101+
} else {
102+
const cursorMoveVX = distX * cursorMoveVCoefficient;
103+
const cursorMoveVY = distY * cursorMoveVCoefficient;
104+
cursorNode.style.left = `${left + cursorMoveVX}px`;
105+
cursorNode.style.top = `${top + cursorMoveVY}px`;
106+
}
107+
}
108+
}
109+
}
110+
cursorFrameId.value = window.requestAnimationFrame(loop);
111+
};
112+
113+
watch(
114+
cursorSize,
115+
() => {
116+
cursorFrameId.value && window.cancelAnimationFrame(cursorFrameId.value);
117+
cursorFrameId.value = window.requestAnimationFrame(loop);
118+
},
119+
{ immediate: true }
120+
);
121+
122+
onUnmounted(() => {
123+
cursorFrameId.value && window.cancelAnimationFrame(cursorFrameId.value);
124+
})
125+
126+
return {
127+
showCursor,
128+
cursorSize,
129+
cursorDefaultRadius,
130+
cursorBigRadius,
131+
onMouseEnter,
132+
onMouseMove,
133+
onMouseLeave,
134+
};
135+
};
136+
137+
export default useCursorMove;

0 commit comments

Comments
 (0)