90 lines
2.3 KiB
JavaScript
90 lines
2.3 KiB
JavaScript
import gsap from 'gsap'
|
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
import { useWindowSize, useEventListener } from '@vueuse/core'
|
|
|
|
/**
|
|
* Shared global raw mouse state (only set up once)
|
|
*/
|
|
const rawMouse = {
|
|
x: ref(0),
|
|
y: ref(0),
|
|
initialized: false,
|
|
cleanup: null,
|
|
}
|
|
|
|
const useGlobalMouseListener = () => {
|
|
if (!rawMouse.initialized && !import.meta.env.SSR) {
|
|
rawMouse.initialized = true
|
|
rawMouse.cleanup = useEventListener(window, 'mousemove', (e) => {
|
|
rawMouse.x.value = e.clientX
|
|
rawMouse.y.value = e.clientY
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Composable for smoothed mouse position with customizable smoothing and normalization.
|
|
*/
|
|
export default (options) => {
|
|
const smoothFactor = options?.smoothFactor ?? 0.1
|
|
const normalize = options?.normalize ?? false
|
|
const callback = options?.onUpdate
|
|
|
|
const { width: wWidth, height: wHeight } = useWindowSize()
|
|
|
|
const sx = ref(0)
|
|
const sy = ref(0)
|
|
|
|
useGlobalMouseListener()
|
|
|
|
const getTargetX = () =>
|
|
normalize ? rawMouse.x.value / wWidth.value : rawMouse.x.value
|
|
const getTargetY = () =>
|
|
normalize ? rawMouse.y.value / wHeight.value : rawMouse.y.value
|
|
|
|
let tween
|
|
onMounted(() => {
|
|
if (tween) tween.kill
|
|
|
|
// Start smoothing tween
|
|
tween = gsap.to(
|
|
{ x: sx.value, y: sy.value },
|
|
{
|
|
duration: 1,
|
|
ease: 'linear',
|
|
repeat: -1,
|
|
onUpdate: () => {
|
|
const newX = gsap.utils.interpolate(
|
|
sx.value,
|
|
getTargetX(),
|
|
smoothFactor,
|
|
)
|
|
const newY = gsap.utils.interpolate(
|
|
sy.value,
|
|
getTargetY(),
|
|
smoothFactor,
|
|
)
|
|
|
|
sx.value = newX
|
|
sy.value = newY
|
|
|
|
callback?.({ sx: sx.value, sy: sy.value })
|
|
},
|
|
},
|
|
)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
tween?.kill()
|
|
rawMouse.cleanup()
|
|
rawMouse.initialized = false
|
|
})
|
|
|
|
return {
|
|
sx,
|
|
sy,
|
|
rawX: rawMouse.x,
|
|
rawY: rawMouse.y,
|
|
}
|
|
}
|