mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
1 line
4.5 KiB
JSON
1 line
4.5 KiB
JSON
{"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"]} |