mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 14:39:30 -07:00
85 lines
2.2 KiB
Vue
85 lines
2.2 KiB
Vue
<template>
|
|
<div ref="magnetRef" :class="wrapperClassName" :style="{ position: 'relative', display: 'inline-block' }"
|
|
v-bind="$attrs">
|
|
<div :class="innerClassName" :style="{
|
|
transform: `translate3d(${position.x}px, ${position.y}px, 0)`,
|
|
transition: transitionStyle,
|
|
willChange: 'transform',
|
|
}">
|
|
<slot />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|
|
|
interface Props {
|
|
padding?: number
|
|
disabled?: boolean
|
|
magnetStrength?: number
|
|
activeTransition?: string
|
|
inactiveTransition?: string
|
|
wrapperClassName?: string
|
|
innerClassName?: string
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
padding: 100,
|
|
disabled: false,
|
|
magnetStrength: 2,
|
|
activeTransition: 'transform 0.3s ease-out',
|
|
inactiveTransition: 'transform 0.5s ease-in-out',
|
|
wrapperClassName: '',
|
|
innerClassName: ''
|
|
})
|
|
|
|
defineOptions({
|
|
inheritAttrs: false
|
|
})
|
|
|
|
const magnetRef = ref<HTMLDivElement | null>(null)
|
|
const isActive = ref(false)
|
|
const position = ref({ x: 0, y: 0 })
|
|
|
|
const transitionStyle = computed(() =>
|
|
isActive.value ? props.activeTransition : props.inactiveTransition
|
|
)
|
|
|
|
const handleMouseMove = (e: MouseEvent) => {
|
|
if (!magnetRef.value || props.disabled) return
|
|
|
|
const { left, top, width, height } = magnetRef.value.getBoundingClientRect()
|
|
const centerX = left + width / 2
|
|
const centerY = top + height / 2
|
|
|
|
const distX = Math.abs(centerX - e.clientX)
|
|
const distY = Math.abs(centerY - e.clientY)
|
|
|
|
if (distX < width / 2 + props.padding && distY < height / 2 + props.padding) {
|
|
isActive.value = true
|
|
const offsetX = (e.clientX - centerX) / props.magnetStrength
|
|
const offsetY = (e.clientY - centerY) / props.magnetStrength
|
|
position.value = { x: offsetX, y: offsetY }
|
|
} else {
|
|
isActive.value = false
|
|
position.value = { x: 0, y: 0 }
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('mousemove', handleMouseMove)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('mousemove', handleMouseMove)
|
|
})
|
|
|
|
watch(() => props.disabled, (newDisabled) => {
|
|
if (newDisabled) {
|
|
position.value = { x: 0, y: 0 }
|
|
isActive.value = false
|
|
}
|
|
})
|
|
</script>
|