mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-09 08:29:30 -06:00
Component Boom
This commit is contained in:
101
src/content/Animations/MagnetLines/MagnetLines.vue
Normal file
101
src/content/Animations/MagnetLines/MagnetLines.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||||
|
||||
interface MagnetLinesProps {
|
||||
rows?: number
|
||||
columns?: number
|
||||
containerSize?: string
|
||||
lineColor?: string
|
||||
lineWidth?: string
|
||||
lineHeight?: string
|
||||
baseAngle?: number
|
||||
className?: string
|
||||
style?: Record<string, string | number>
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<MagnetLinesProps>(), {
|
||||
rows: 9,
|
||||
columns: 9,
|
||||
containerSize: '80vmin',
|
||||
lineColor: '#efefef',
|
||||
lineWidth: '1vmin',
|
||||
lineHeight: '6vmin',
|
||||
baseAngle: -10,
|
||||
className: '',
|
||||
style: () => ({})
|
||||
})
|
||||
|
||||
const containerRef = ref<HTMLDivElement | null>(null)
|
||||
|
||||
const total = computed(() => props.rows * props.columns)
|
||||
|
||||
const onPointerMove = (pointer: { x: number; y: number }) => {
|
||||
const container = containerRef.value
|
||||
if (!container) return
|
||||
|
||||
const items = container.querySelectorAll<HTMLSpanElement>('span')
|
||||
|
||||
items.forEach((item) => {
|
||||
const rect = item.getBoundingClientRect()
|
||||
const centerX = rect.x + rect.width / 2
|
||||
const centerY = rect.y + rect.height / 2
|
||||
|
||||
const b = pointer.x - centerX
|
||||
const a = pointer.y - centerY
|
||||
const c = Math.sqrt(a * a + b * b) || 1
|
||||
const r = ((Math.acos(b / c) * 180) / Math.PI) * (pointer.y > centerY ? 1 : -1)
|
||||
|
||||
item.style.setProperty('--rotate', `${r}deg`)
|
||||
})
|
||||
}
|
||||
|
||||
const handlePointerMove = (e: PointerEvent) => {
|
||||
onPointerMove({ x: e.x, y: e.y })
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const container = containerRef.value
|
||||
if (!container) return
|
||||
|
||||
window.addEventListener('pointermove', handlePointerMove)
|
||||
|
||||
const items = container.querySelectorAll<HTMLSpanElement>('span')
|
||||
if (items.length) {
|
||||
const middleIndex = Math.floor(items.length / 2)
|
||||
const rect = items[middleIndex].getBoundingClientRect()
|
||||
onPointerMove({ x: rect.x, y: rect.y })
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('pointermove', handlePointerMove)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="containerRef"
|
||||
:class="`grid place-items-center ${props.className}`"
|
||||
:style="{
|
||||
gridTemplateColumns: `repeat(${props.columns}, 1fr)`,
|
||||
gridTemplateRows: `repeat(${props.rows}, 1fr)`,
|
||||
width: props.containerSize,
|
||||
height: props.containerSize,
|
||||
...props.style,
|
||||
}"
|
||||
>
|
||||
<span
|
||||
v-for="i in total"
|
||||
:key="i"
|
||||
class="block origin-center"
|
||||
:style="{
|
||||
backgroundColor: props.lineColor,
|
||||
width: props.lineWidth,
|
||||
height: props.lineHeight,
|
||||
'--rotate': `${props.baseAngle}deg`,
|
||||
transform: 'rotate(var(--rotate))',
|
||||
willChange: 'transform',
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user