mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
1 line
3.7 KiB
JSON
1 line
3.7 KiB
JSON
{"name":"AnimatedContent","title":"AnimatedContent","description":"Wrapper that animates any children on scroll or mount with configurable direction, distance, duration and easing.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, watch, useTemplateRef } from 'vue';\nimport { gsap } from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\n\ngsap.registerPlugin(ScrollTrigger);\n\ninterface AnimatedContentProps {\n distance?: number;\n direction?: 'vertical' | 'horizontal';\n reverse?: boolean;\n duration?: number;\n ease?: string | ((progress: number) => number);\n initialOpacity?: number;\n animateOpacity?: boolean;\n scale?: number;\n threshold?: number;\n delay?: number;\n className?: string;\n}\n\nconst props = withDefaults(defineProps<AnimatedContentProps>(), {\n distance: 100,\n direction: 'vertical',\n reverse: false,\n duration: 0.8,\n ease: 'power3.out',\n initialOpacity: 0,\n animateOpacity: true,\n scale: 1,\n threshold: 0.1,\n delay: 0,\n className: ''\n});\n\nconst emit = defineEmits<{\n complete: [];\n}>();\n\nconst containerRef = useTemplateRef<HTMLDivElement>('containerRef');\n\nonMounted(() => {\n const el = containerRef.value;\n if (!el) return;\n\n const axis = props.direction === 'horizontal' ? 'x' : 'y';\n const offset = props.reverse ? -props.distance : props.distance;\n const startPct = (1 - props.threshold) * 100;\n\n gsap.set(el, {\n [axis]: offset,\n scale: props.scale,\n opacity: props.animateOpacity ? props.initialOpacity : 1\n });\n\n gsap.to(el, {\n [axis]: 0,\n scale: 1,\n opacity: 1,\n duration: props.duration,\n ease: props.ease,\n delay: props.delay,\n onComplete: () => emit('complete'),\n scrollTrigger: {\n trigger: el,\n start: `top ${startPct}%`,\n toggleActions: 'play none none none',\n once: true\n }\n });\n});\n\nwatch(\n () => [\n props.distance,\n props.direction,\n props.reverse,\n props.duration,\n props.ease,\n props.initialOpacity,\n props.animateOpacity,\n props.scale,\n props.threshold,\n props.delay\n ],\n () => {\n const el = containerRef.value;\n if (!el) return;\n\n ScrollTrigger.getAll().forEach(t => t.kill());\n gsap.killTweensOf(el);\n\n const axis = props.direction === 'horizontal' ? 'x' : 'y';\n const offset = props.reverse ? -props.distance : props.distance;\n const startPct = (1 - props.threshold) * 100;\n\n gsap.set(el, {\n [axis]: offset,\n scale: props.scale,\n opacity: props.animateOpacity ? props.initialOpacity : 1\n });\n\n gsap.to(el, {\n [axis]: 0,\n scale: 1,\n opacity: 1,\n duration: props.duration,\n ease: props.ease,\n delay: props.delay,\n onComplete: () => emit('complete'),\n scrollTrigger: {\n trigger: el,\n start: `top ${startPct}%`,\n toggleActions: 'play none none none',\n once: true\n }\n });\n },\n { deep: true }\n);\n\nonUnmounted(() => {\n const el = containerRef.value;\n if (el) {\n ScrollTrigger.getAll().forEach(t => t.kill());\n gsap.killTweensOf(el);\n }\n});\n</script>\n\n<template>\n <div ref=\"containerRef\" :class=\"`animated-content ${props.className}`\">\n <slot />\n </div>\n</template>\n\n<style scoped>\n/* GSAP will handle all transforms and opacity */\n</style>\n","path":"AnimatedContent/AnimatedContent.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"gsap","version":"^3.13.0"}],"devDependencies":[],"categories":["Animations"]} |