Component Boom

This commit is contained in:
David Haz
2025-07-10 15:36:38 +03:00
parent a4982577ad
commit 9b3465b04d
135 changed files with 16697 additions and 60 deletions

View File

@@ -0,0 +1,129 @@
<script setup lang="ts">
import { computed, ref, watchEffect, onUnmounted } from 'vue'
import { Motion } from 'motion-v'
interface CircularTextProps {
text: string
spinDuration?: number
onHover?: 'slowDown' | 'speedUp' | 'pause' | 'goBonkers'
className?: string
}
const props = withDefaults(defineProps<CircularTextProps>(), {
text: '',
spinDuration: 20,
onHover: 'speedUp',
className: ''
})
const letters = computed(() => Array.from(props.text))
const isHovered = ref(false)
const currentRotation = ref(0)
const animationId = ref<number | null>(null)
const lastTime = ref<number>(Date.now())
const rotationSpeed = ref<number>(0)
const getCurrentSpeed = () => {
if (isHovered.value && props.onHover === 'pause') return 0
const baseDuration = props.spinDuration
const baseSpeed = 360 / baseDuration
if (!isHovered.value) return baseSpeed
switch (props.onHover) {
case 'slowDown':
return baseSpeed / 2
case 'speedUp':
return baseSpeed * 4
case 'goBonkers':
return baseSpeed * 20
default:
return baseSpeed
}
}
const getCurrentScale = () => {
return (isHovered.value && props.onHover === 'goBonkers') ? 0.8 : 1
}
const animate = () => {
const now = Date.now()
const deltaTime = (now - lastTime.value) / 1000
lastTime.value = now
const targetSpeed = getCurrentSpeed()
const speedDiff = targetSpeed - rotationSpeed.value
const smoothingFactor = Math.min(1, deltaTime * 5)
rotationSpeed.value += speedDiff * smoothingFactor
currentRotation.value = (currentRotation.value + rotationSpeed.value * deltaTime) % 360
animationId.value = requestAnimationFrame(animate)
}
const startAnimation = () => {
if (animationId.value) {
cancelAnimationFrame(animationId.value)
}
lastTime.value = Date.now()
rotationSpeed.value = getCurrentSpeed()
animate()
}
watchEffect(() => {
startAnimation()
})
startAnimation()
onUnmounted(() => {
if (animationId.value) {
cancelAnimationFrame(animationId.value)
}
})
const handleHoverStart = () => {
isHovered.value = true
}
const handleHoverEnd = () => {
isHovered.value = false
}
const getLetterTransform = (index: number) => {
const rotationDeg = (360 / letters.value.length) * index
const factor = Math.PI / letters.value.length
const x = factor * index
const y = factor * index
return `rotateZ(${rotationDeg}deg) translate3d(${x}px, ${y}px, 0)`
}
</script>
<template>
<Motion :animate="{
rotate: currentRotation,
scale: getCurrentScale()
}" :transition="{
rotate: {
duration: 0
},
scale: {
type: 'spring',
damping: 20,
stiffness: 300
}
}"
:class="`m-0 mx-auto rounded-full w-[200px] h-[200px] relative font-black text-white text-center cursor-pointer origin-center ${props.className}`"
@mouseenter="handleHoverStart" @mouseleave="handleHoverEnd">
<span v-for="(letter, i) in letters" :key="i"
class="absolute inline-block inset-0 text-2xl transition-all duration-500 ease-[cubic-bezier(0,0,0,1)]" :style="{
transform: getLetterTransform(i),
WebkitTransform: getLetterTransform(i)
}">
{{ letter }}
</span>
</Motion>
</template>