mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
[ REFACT ] : TextAnimations Registry Changes
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"name":"CircularText","title":"CircularText","description":"Layouts characters around a circle with optional rotation animation.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { computed, ref, watchEffect, onUnmounted } from 'vue';\nimport { Motion } from 'motion-v';\n\ninterface CircularTextProps {\n text: string;\n spinDuration?: number;\n onHover?: 'slowDown' | 'speedUp' | 'pause' | 'goBonkers';\n className?: string;\n}\n\nconst props = withDefaults(defineProps<CircularTextProps>(), {\n text: '',\n spinDuration: 20,\n onHover: 'speedUp',\n className: ''\n});\n\nconst letters = computed(() => Array.from(props.text));\nconst isHovered = ref(false);\n\nconst currentRotation = ref(0);\nconst animationId = ref<number | null>(null);\nconst lastTime = ref<number>(Date.now());\nconst rotationSpeed = ref<number>(0);\n\nconst getCurrentSpeed = () => {\n if (isHovered.value && props.onHover === 'pause') return 0;\n\n const baseDuration = props.spinDuration;\n const baseSpeed = 360 / baseDuration;\n\n if (!isHovered.value) return baseSpeed;\n\n switch (props.onHover) {\n case 'slowDown':\n return baseSpeed / 2;\n case 'speedUp':\n return baseSpeed * 4;\n case 'goBonkers':\n return baseSpeed * 20;\n default:\n return baseSpeed;\n }\n};\n\nconst getCurrentScale = () => {\n return isHovered.value && props.onHover === 'goBonkers' ? 0.8 : 1;\n};\n\nconst animate = () => {\n const now = Date.now();\n const deltaTime = (now - lastTime.value) / 1000;\n lastTime.value = now;\n\n const targetSpeed = getCurrentSpeed();\n\n const speedDiff = targetSpeed - rotationSpeed.value;\n const smoothingFactor = Math.min(1, deltaTime * 5);\n rotationSpeed.value += speedDiff * smoothingFactor;\n\n currentRotation.value = (currentRotation.value + rotationSpeed.value * deltaTime) % 360;\n\n animationId.value = requestAnimationFrame(animate);\n};\n\nconst startAnimation = () => {\n if (animationId.value) {\n cancelAnimationFrame(animationId.value);\n }\n lastTime.value = Date.now();\n rotationSpeed.value = getCurrentSpeed();\n animate();\n};\n\nwatchEffect(() => {\n startAnimation();\n});\n\nstartAnimation();\n\nonUnmounted(() => {\n if (animationId.value) {\n cancelAnimationFrame(animationId.value);\n }\n});\n\nconst handleHoverStart = () => {\n isHovered.value = true;\n};\n\nconst handleHoverEnd = () => {\n isHovered.value = false;\n};\n\nconst getLetterTransform = (index: number) => {\n const rotationDeg = (360 / letters.value.length) * index;\n const factor = Math.PI / letters.value.length;\n const x = factor * index;\n const y = factor * index;\n return `rotateZ(${rotationDeg}deg) translate3d(${x}px, ${y}px, 0)`;\n};\n</script>\n\n<template>\n <Motion\n :animate=\"{\n rotate: currentRotation,\n scale: getCurrentScale()\n }\"\n :transition=\"{\n rotate: {\n duration: 0\n },\n scale: {\n type: 'spring',\n damping: 20,\n stiffness: 300\n }\n }\"\n :class=\"`m-0 mx-auto rounded-full w-[200px] h-[200px] relative font-black text-white text-center cursor-pointer origin-center ${props.className}`\"\n @mouseenter=\"handleHoverStart\"\n @mouseleave=\"handleHoverEnd\"\n >\n <span\n v-for=\"(letter, i) in letters\"\n :key=\"i\"\n class=\"absolute inline-block inset-0 text-2xl transition-all duration-500 ease-[cubic-bezier(0,0,0,1)]\"\n :style=\"{\n transform: getLetterTransform(i),\n WebkitTransform: getLetterTransform(i)\n }\"\n >\n {{ letter }}\n </span>\n </Motion>\n</template>\n","path":"CircularText/CircularText.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"motion-v","version":"^1.5.0"}],"devDependencies":[],"categories":["TextAnimations"]}
|
||||
{"name":"CircularText","title":"CircularText","description":"Layouts characters around a circle with optional rotation animation.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { animate, Motion, MotionValue, useMotionValue } from 'motion-v';\nimport { computed, onMounted, watch } from 'vue';\n\ninterface CircularTextProps {\n text: string;\n spinDuration?: number;\n onHover?: 'slowDown' | 'speedUp' | 'pause' | 'goBonkers';\n className?: string;\n}\n\nconst props = withDefaults(defineProps<CircularTextProps>(), {\n spinDuration: 20,\n onHover: 'speedUp',\n className: ''\n});\n\nconst letters = computed(() => Array.from(props.text));\nconst rotation: MotionValue<number> = useMotionValue(0);\n\nlet currentAnimation: ReturnType<typeof animate> | null = null;\n\nconst startRotation = (duration: number) => {\n currentAnimation?.stop();\n const start = rotation.get();\n\n currentAnimation = animate(rotation, start + 360, {\n duration,\n ease: 'linear',\n repeat: Infinity\n });\n};\n\nonMounted(() => {\n startRotation(props.spinDuration);\n});\n\nwatch(\n () => [props.spinDuration, props.text],\n () => {\n startRotation(props.spinDuration);\n }\n);\n\nconst handleHoverStart = () => {\n if (!props.onHover) return;\n\n switch (props.onHover) {\n case 'slowDown':\n startRotation(props.spinDuration * 2);\n break;\n case 'speedUp':\n startRotation(props.spinDuration / 4);\n break;\n case 'pause':\n currentAnimation?.stop();\n break;\n case 'goBonkers':\n startRotation(props.spinDuration / 20);\n break;\n }\n};\n\nconst handleHoverEnd = () => {\n startRotation(props.spinDuration);\n};\n\nconst getLetterTransform = (index: number) => {\n const rotationDeg = (360 / letters.value.length) * index;\n const factor = Math.PI / letters.value.length;\n const x = factor * index;\n const y = factor * index;\n return `rotateZ(${rotationDeg}deg) translate3d(${x}px, ${y}px, 0)`;\n};\n</script>\n\n<template>\n <Motion\n tag=\"div\"\n :class=\"[\n 'm-0 mx-auto rounded-full w-[200px] h-[200px] relative font-black text-white text-center cursor-pointer origin-center',\n className\n ]\"\n :style=\"{\n rotate: rotation\n }\"\n :initial=\"{\n rotate: 0\n }\"\n @mouseenter=\"handleHoverStart\"\n @mouseleave=\"handleHoverEnd\"\n >\n <span\n v-for=\"(letter, i) in letters\"\n :key=\"i\"\n class=\"inline-block absolute inset-0 text-2xl transition-all duration-500 ease-[cubic-bezier(0,0,0,1)]\"\n :style=\"{\n transform: getLetterTransform(i),\n WebkitTransform: getLetterTransform(i)\n }\"\n >\n {{ letter }}\n </span>\n </Motion>\n</template>\n","path":"CircularText/CircularText.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"motion-v","version":"^1.10.2"}],"devDependencies":[],"categories":["TextAnimations"]}
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"name":"GradientText","title":"GradientText","description":"Animated gradient sweep across live text with speed and color control.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\ninterface GradientTextProps {\n text: string;\n className?: string;\n colors?: string[];\n animationSpeed?: number;\n showBorder?: boolean;\n}\n\nconst props = withDefaults(defineProps<GradientTextProps>(), {\n text: '',\n className: '',\n colors: () => ['#ffaa40', '#9c40ff', '#ffaa40'],\n animationSpeed: 8,\n showBorder: false\n});\n\nconst gradientStyle = computed(() => ({\n backgroundImage: `linear-gradient(to right, ${props.colors.join(', ')})`,\n animationDuration: `${props.animationSpeed}s`,\n backgroundSize: '300% 100%',\n '--animation-duration': `${props.animationSpeed}s`\n}));\n\nconst borderStyle = computed(() => ({\n ...gradientStyle.value\n}));\n\nconst textStyle = computed(() => ({\n ...gradientStyle.value,\n backgroundClip: 'text',\n WebkitBackgroundClip: 'text'\n}));\n</script>\n\n<template>\n <div\n :class=\"`relative mx-auto flex max-w-fit flex-row items-center justify-center rounded-[1.25rem] font-medium backdrop-blur transition-shadow duration-500 overflow-hidden cursor-pointer ${className}`\"\n >\n <div\n v-if=\"showBorder\"\n class=\"absolute inset-0 bg-cover z-0 pointer-events-none animate-gradient\"\n :style=\"borderStyle\"\n >\n <div\n class=\"absolute inset-0 bg-black rounded-[1.25rem] z-[-1]\"\n style=\"width: calc(100% - 2px); height: calc(100% - 2px); left: 50%; top: 50%; transform: translate(-50%, -50%)\"\n />\n </div>\n\n <div class=\"inline-block relative z-2 text-transparent bg-cover animate-gradient\" :style=\"textStyle\">\n {{ text }}\n </div>\n </div>\n</template>\n\n<style scoped>\n@keyframes gradient {\n 0% {\n background-position: 0% 50%;\n }\n 50% {\n background-position: 100% 50%;\n }\n 100% {\n background-position: 0% 50%;\n }\n}\n\n.animate-gradient {\n animation: gradient var(--animation-duration, 8s) linear infinite;\n}\n</style>\n","path":"GradientText/GradientText.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[],"devDependencies":[],"categories":["TextAnimations"]}
|
||||
{"name":"GradientText","title":"GradientText","description":"Animated gradient sweep across live text with speed and color control.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { Motion, useAnimationFrame, useMotionValue, useTransform } from 'motion-v';\nimport { computed, ref, useSlots } from 'vue';\n\ninterface GradientTextProps {\n className?: string;\n colors?: string[];\n animationSpeed?: number;\n showBorder?: boolean;\n direction?: 'horizontal' | 'vertical' | 'diagonal';\n pauseOnHover?: boolean;\n yoyo?: boolean;\n}\n\nconst props = withDefaults(defineProps<GradientTextProps>(), {\n className: '',\n colors: () => ['#27FF64', '#27FF64', '#A0FFBC'],\n animationSpeed: 8,\n showBorder: false,\n direction: 'horizontal',\n pauseOnHover: false,\n yoyo: true\n});\n\nconst slots = useSlots();\nconst text = computed(() => (slots.default?.() ?? []).map(v => v.children).join(''));\n\nconst isPaused = ref(false);\nconst progress = useMotionValue(0);\nconst elapsedRef = ref(0);\nconst lastTimeRef = ref<number | null>(null);\n\nconst animationDuration = props.animationSpeed * 1000;\n\nuseAnimationFrame(time => {\n if (isPaused.value) {\n lastTimeRef.value = null;\n return;\n }\n\n if (lastTimeRef.value === null) {\n lastTimeRef.value = time;\n return;\n }\n\n const deltaTime = time - lastTimeRef.value;\n lastTimeRef.value = time;\n elapsedRef.value += deltaTime;\n\n if (props.yoyo) {\n const fullCycle = animationDuration * 2;\n const cycleTime = elapsedRef.value % fullCycle;\n\n if (cycleTime < animationDuration) {\n progress.set((cycleTime / animationDuration) * 100);\n } else {\n progress.set(100 - ((cycleTime - animationDuration) / animationDuration) * 100);\n }\n } else {\n // Continuously increase position for seamless looping\n progress.set((elapsedRef.value / animationDuration) * 100);\n }\n});\n\nconst backgroundPosition = useTransform(progress, p => {\n if (props.direction === 'horizontal') {\n return `${p}% 50%`;\n } else if (props.direction === 'vertical') {\n return `50% ${p}%`;\n } else {\n // For diagonal, move only horizontally to avoid interference patterns\n return `${p}% 50%`;\n }\n});\n\nconst handleMouseEnter = () => {\n if (props.pauseOnHover) isPaused.value = true;\n};\n\nconst handleMouseLeave = () => {\n if (props.pauseOnHover) isPaused.value = false;\n};\n\nconst gradientAngle = computed(() =>\n props.direction === 'horizontal' ? 'to right' : props.direction === 'vertical' ? 'to bottom' : 'to bottom right'\n);\n\n// Duplicate first color at the end for seamless looping\nconst gradientColors = computed(() => [...props.colors, props.colors[0]].join(', '));\n\nconst gradientStyle = computed(() => ({\n backgroundImage: `linear-gradient(${gradientAngle.value}, ${gradientColors.value})`,\n backgroundSize:\n props.direction === 'horizontal' ? '300% 100%' : props.direction === 'vertical' ? '100% 300%' : '300% 300%',\n backgroundRepeat: 'repeat'\n}));\n</script>\n\n<template>\n <Motion\n tag=\"div\"\n :class=\"[\n 'relative mx-auto flex max-w-fit flex-row items-center justify-center rounded-[1.25rem] font-medium backdrop-blur transition-shadow duration-500 overflow-hidden cursor-pointer',\n className,\n showBorder && 'py-1 px-2'\n ]\"\n @mouseenter=\"handleMouseEnter\"\n @mouseleave=\"handleMouseLeave\"\n >\n <Motion\n tag=\"div\"\n v-if=\"showBorder\"\n class=\"z-0 absolute inset-0 rounded-[1.25rem] pointer-events-none\"\n :style=\"{ ...gradientStyle, backgroundPosition }\"\n >\n <div\n class=\"z-[-1] absolute bg-black rounded-[1.25rem]\"\n :style=\"{\n width: 'calc(100% - 2px)',\n height: 'calc(100% - 2px)',\n left: '50%',\n top: '50%',\n transform: 'translate(-50%, -50%)'\n }\"\n />\n </Motion>\n\n <Motion\n tag=\"div\"\n class=\"inline-block z-2 relative bg-clip-text text-transparent\"\n :style=\"{\n ...gradientStyle,\n backgroundPosition,\n WebkitBackgroundClip: 'text'\n }\"\n >\n {{ text }}\n </Motion>\n </Motion>\n</template>\n","path":"GradientText/GradientText.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"motion-v","version":"^1.10.2"}],"devDependencies":[],"categories":["TextAnimations"]}
|
||||
@@ -1 +1 @@
|
||||
{"name":"ShinyText","title":"ShinyText","description":"Metallic sheen sweeps across text producing a reflective highlight.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\ninterface ShinyTextProps {\n text: string;\n disabled?: boolean;\n speed?: number;\n className?: string;\n}\n\nconst props = withDefaults(defineProps<ShinyTextProps>(), {\n text: '',\n disabled: false,\n speed: 5,\n className: ''\n});\n\nconst animationDuration = computed(() => `${props.speed}s`);\n</script>\n\n<template>\n <div\n :class=\"`text-[#b5b5b5a4] bg-clip-text inline-block ${!props.disabled ? 'animate-shine' : ''} ${props.className}`\"\n :style=\"{\n backgroundImage:\n 'linear-gradient(120deg, rgba(255, 255, 255, 0) 40%, rgba(255, 255, 255, 0.8) 50%, rgba(255, 255, 255, 0) 60%)',\n backgroundSize: '200% 100%',\n WebkitBackgroundClip: 'text',\n animationDuration: animationDuration\n }\"\n >\n {{ props.text }}\n </div>\n</template>\n\n<style scoped>\n@keyframes shine {\n 0% {\n background-position: 100%;\n }\n 100% {\n background-position: -100%;\n }\n}\n\n.animate-shine {\n animation: shine 5s linear infinite;\n}\n</style>\n","path":"ShinyText/ShinyText.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[],"devDependencies":[],"categories":["TextAnimations"]}
|
||||
{"name":"ShinyText","title":"ShinyText","description":"Metallic sheen sweeps across text producing a reflective highlight.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { Motion, useAnimationFrame, useMotionValue, useTransform } from 'motion-v';\nimport { computed, ref, watch } from 'vue';\n\ninterface ShinyTextProps {\n text: string;\n disabled?: boolean;\n speed?: number;\n className?: string;\n color?: string;\n shineColor?: string;\n spread?: number;\n yoyo?: boolean;\n pauseOnHover?: boolean;\n direction?: 'left' | 'right';\n delay?: number;\n}\n\nconst props = withDefaults(defineProps<ShinyTextProps>(), {\n disabled: false,\n speed: 2,\n className: '',\n color: '#b5b5b5',\n shineColor: '#ffffff',\n spread: 120,\n yoyo: false,\n pauseOnHover: false,\n direction: 'left',\n delay: 0\n});\n\nconst isPaused = ref(false);\nconst progress = useMotionValue(0);\nconst elapsedRef = ref(0);\nconst lastTimeRef = ref<number | null>(null);\nconst directionRef = ref(props.direction === 'left' ? 1 : -1);\n\nconst animationDuration = computed(() => props.speed * 1000);\nconst delayDuration = computed(() => props.delay * 1000);\n\nuseAnimationFrame(time => {\n if (props.disabled || isPaused.value) {\n lastTimeRef.value = null;\n return;\n }\n\n if (lastTimeRef.value === null) {\n lastTimeRef.value = time;\n return;\n }\n\n const deltaTime = time - lastTimeRef.value;\n lastTimeRef.value = time;\n\n elapsedRef.value += deltaTime;\n\n // Animation goes from 0 to 100\n if (props.yoyo) {\n const cycleDuration = animationDuration.value + delayDuration.value;\n const fullCycle = cycleDuration * 2;\n const cycleTime = elapsedRef.value % fullCycle;\n\n if (cycleTime < animationDuration.value) {\n // Forward animation: 0 -> 100\n const p = (cycleTime / animationDuration.value) * 100;\n progress.set(directionRef.value === 1 ? p : 100 - p);\n } else if (cycleTime < cycleDuration) {\n // Delay at end\n progress.set(directionRef.value === 1 ? 100 : 0);\n } else if (cycleTime < cycleDuration + animationDuration.value) {\n // Reverse animation: 100 -> 0\n const reverseTime = cycleTime - cycleDuration;\n const p = 100 - (reverseTime / animationDuration.value) * 100;\n progress.set(directionRef.value === 1 ? p : 100 - p);\n } else {\n // Delay at start\n progress.set(directionRef.value === 1 ? 0 : 100);\n }\n } else {\n const cycleDuration = animationDuration.value + delayDuration.value;\n const cycleTime = elapsedRef.value % cycleDuration;\n\n if (cycleTime < animationDuration.value) {\n // Animation phase: 0 -> 100\n const p = (cycleTime / animationDuration.value) * 100;\n progress.set(directionRef.value === 1 ? p : 100 - p);\n } else {\n // Delay phase - hold at end (shine off-screen)\n progress.set(directionRef.value === 1 ? 100 : 0);\n }\n }\n});\n\nwatch(\n () => props.direction,\n () => {\n directionRef.value = props.direction === 'left' ? 1 : -1;\n elapsedRef.value = 0;\n progress.set(0);\n },\n {\n immediate: true\n }\n);\n\nconst backgroundPosition = useTransform(progress, p => `${150 - p * 2}% center`);\n\nconst handleMouseEnter = () => {\n if (props.pauseOnHover) isPaused.value = true;\n};\n\nconst handleMouseLeave = () => {\n if (props.pauseOnHover) isPaused.value = false;\n};\n\nconst gradientStyle = computed(() => ({\n backgroundImage: `linear-gradient(${props.spread}deg, ${props.color} 0%, ${props.color} 35%, ${props.shineColor} 50%, ${props.color} 65%, ${props.color} 100%)`,\n backgroundSize: '200% auto',\n WebkitBackgroundClip: 'text',\n backgroundClip: 'text',\n WebkitTextFillColor: 'transparent'\n}));\n</script>\n\n<template>\n <Motion\n tag=\"span\"\n :class=\"['inline-block', className]\"\n :style=\"{ ...gradientStyle, backgroundPosition }\"\n @mouseenter=\"handleMouseEnter\"\n @mouseleave=\"handleMouseLeave\"\n >\n {{ text }}\n </Motion>\n</template>\n","path":"ShinyText/ShinyText.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"motion-v","version":"^1.10.2"}],"devDependencies":[],"categories":["TextAnimations"]}
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user