mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
1 line
3.3 KiB
JSON
1 line
3.3 KiB
JSON
{"name":"ScrollFloat","title":"ScrollFloat","description":"Text gently floats / parallax shifts on scroll.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<template>\n <h2 ref=\"containerRef\" :class=\"`overflow-hidden ${containerClassName}`\">\n <span\n :class=\"`inline-block text-center leading-relaxed font-black ${textClassName}`\"\n style=\"font-size: clamp(1.6rem, 8vw, 10rem)\"\n >\n <span v-for=\"(char, index) in splitText\" :key=\"index\" class=\"inline-block char\">\n {{ char === ' ' ? '\\u00A0' : char }}\n </span>\n </span>\n </h2>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, watch, useTemplateRef } from 'vue';\nimport { gsap } from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\n\ngsap.registerPlugin(ScrollTrigger);\n\ninterface Props {\n children: string;\n scrollContainerRef?: { current: HTMLElement | null };\n containerClassName?: string;\n textClassName?: string;\n animationDuration?: number;\n ease?: string;\n scrollStart?: string;\n scrollEnd?: string;\n stagger?: number;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n containerClassName: '',\n textClassName: '',\n animationDuration: 1,\n ease: 'back.inOut(2)',\n scrollStart: 'center bottom+=50%',\n scrollEnd: 'bottom bottom-=40%',\n stagger: 0.03\n});\n\nconst containerRef = useTemplateRef<HTMLElement>('containerRef');\nlet scrollTriggerInstance: ScrollTrigger | null = null;\n\nconst splitText = computed(() => {\n const text = typeof props.children === 'string' ? props.children : '';\n return text.split('');\n});\n\nconst initializeAnimation = () => {\n const el = containerRef.value;\n if (!el) return;\n\n const scroller =\n props.scrollContainerRef && props.scrollContainerRef.current ? props.scrollContainerRef.current : window;\n\n const charElements = el.querySelectorAll('.char');\n\n if (scrollTriggerInstance) {\n scrollTriggerInstance.kill();\n }\n\n const tl = gsap.fromTo(\n charElements,\n {\n willChange: 'opacity, transform',\n opacity: 0,\n yPercent: 120,\n scaleY: 2.3,\n scaleX: 0.7,\n transformOrigin: '50% 0%'\n },\n {\n duration: props.animationDuration,\n ease: props.ease,\n opacity: 1,\n yPercent: 0,\n scaleY: 1,\n scaleX: 1,\n stagger: props.stagger,\n scrollTrigger: {\n trigger: el,\n scroller,\n start: props.scrollStart,\n end: props.scrollEnd,\n scrub: true\n }\n }\n );\n\n scrollTriggerInstance = tl.scrollTrigger || null;\n};\n\nonMounted(() => {\n initializeAnimation();\n});\n\nonUnmounted(() => {\n if (scrollTriggerInstance) {\n scrollTriggerInstance.kill();\n }\n});\n\nwatch(\n [\n () => props.children,\n () => props.scrollContainerRef,\n () => props.animationDuration,\n () => props.ease,\n () => props.scrollStart,\n () => props.scrollEnd,\n () => props.stagger\n ],\n () => {\n initializeAnimation();\n },\n { deep: true }\n);\n</script>\n","path":"ScrollFloat/ScrollFloat.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"gsap","version":"^3.13.0"}],"devDependencies":[],"categories":["TextAnimations"]} |