Issue resolved

Signed-off-by: zubairrafi <walleeva2018@gmail.com>
This commit is contained in:
zubairrafi
2025-07-12 11:26:58 +06:00
parent ea8a04eb3b
commit 936bca258a
3 changed files with 118 additions and 138 deletions

View File

@@ -2,7 +2,7 @@ import code from '@content/TextAnimations/ScrollFloat/ScrollFloat.vue?raw'
import type { CodeObject } from '../../../types/code' import type { CodeObject } from '../../../types/code'
export const scrollFloatCode: CodeObject = { export const scrollFloatCode: CodeObject = {
cli: `npx jsrepo add https://vue-bits.dev/ui/Components/ProfileCard`, cli: `npx jsrepo add https://vue-bits.dev/ui/TextAnimations/ScrollFloat`,
usage: `<template> usage: `<template>
<ScrollFloat <ScrollFloat
:children="scrollText" :children="scrollText"

View File

@@ -1,141 +1,123 @@
<template> <template>
<h2 ref="containerRef" :class="`scroll-float ${containerClassName}`"> <h2 ref="containerRef" :class="`overflow-hidden ${containerClassName}`">
<span :class="`scroll-float-text ${textClassName}`"> <span :class="`inline-block text-center leading-relaxed font-black ${textClassName}`" style="font-size: clamp(1.6rem, 8vw, 10rem);">
<span <span
v-for="(char, index) in splitText" v-for="(char, index) in splitText"
:key="index" :key="index"
class="char" class="inline-block char"
> >
{{ char === " " ? "\u00A0" : char }} {{ char === " " ? "\u00A0" : char }}
</span>
</span> </span>
</h2> </span>
</template> </h2>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'; import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
import { gsap } from 'gsap'; import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger'; import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger); gsap.registerPlugin(ScrollTrigger);
interface Props { interface Props {
children: string; children: string;
scrollContainerRef?: { current: HTMLElement | null }; scrollContainerRef?: { current: HTMLElement | null };
containerClassName?: string; containerClassName?: string;
textClassName?: string; textClassName?: string;
animationDuration?: number; animationDuration?: number;
ease?: string; ease?: string;
scrollStart?: string; scrollStart?: string;
scrollEnd?: string; scrollEnd?: string;
stagger?: number; stagger?: number;
}
const props = withDefaults(defineProps<Props>(), {
containerClassName: '',
textClassName: '',
animationDuration: 1,
ease: 'back.inOut(2)',
scrollStart: 'center bottom+=50%',
scrollEnd: 'bottom bottom-=40%',
stagger: 0.03
});
const containerRef = ref<HTMLElement | null>(null);
let scrollTriggerInstance: ScrollTrigger | null = null;
const splitText = computed(() => {
const text = typeof props.children === 'string' ? props.children : '';
return text.split("");
});
const initializeAnimation = () => {
const el = containerRef.value;
if (!el) return;
const scroller =
props.scrollContainerRef && props.scrollContainerRef.current
? props.scrollContainerRef.current
: window;
const charElements = el.querySelectorAll('.char');
if (scrollTriggerInstance) {
scrollTriggerInstance.kill();
} }
const props = withDefaults(defineProps<Props>(), { const tl = gsap.fromTo(
containerClassName: '', charElements,
textClassName: '', {
animationDuration: 1, willChange: 'opacity, transform',
ease: 'back.inOut(2)', opacity: 0,
scrollStart: 'center bottom+=50%', yPercent: 120,
scrollEnd: 'bottom bottom-=40%', scaleY: 2.3,
stagger: 0.03 scaleX: 0.7,
}); transformOrigin: '50% 0%'
const containerRef = ref<HTMLElement | null>(null);
let scrollTriggerInstance: ScrollTrigger | null = null;
const splitText = computed(() => {
const text = typeof props.children === 'string' ? props.children : '';
return text.split("");
});
const initializeAnimation = () => {
const el = containerRef.value;
if (!el) return;
const scroller =
props.scrollContainerRef && props.scrollContainerRef.current
? props.scrollContainerRef.current
: window;
const charElements = el.querySelectorAll('.char');
if (scrollTriggerInstance) {
scrollTriggerInstance.kill();
}
const tl = gsap.fromTo(
charElements,
{
willChange: 'opacity, transform',
opacity: 0,
yPercent: 120,
scaleY: 2.3,
scaleX: 0.7,
transformOrigin: '50% 0%'
},
{
duration: props.animationDuration,
ease: props.ease,
opacity: 1,
yPercent: 0,
scaleY: 1,
scaleX: 1,
stagger: props.stagger,
scrollTrigger: {
trigger: el,
scroller,
start: props.scrollStart,
end: props.scrollEnd,
scrub: true
}
}
);
scrollTriggerInstance = tl.scrollTrigger || null;
};
onMounted(() => {
initializeAnimation();
});
onUnmounted(() => {
if (scrollTriggerInstance) {
scrollTriggerInstance.kill();
}
});
watch(
[
() => props.children,
() => props.scrollContainerRef,
() => props.animationDuration,
() => props.ease,
() => props.scrollStart,
() => props.scrollEnd,
() => props.stagger
],
() => {
initializeAnimation();
}, },
{ deep: true } {
duration: props.animationDuration,
ease: props.ease,
opacity: 1,
yPercent: 0,
scaleY: 1,
scaleX: 1,
stagger: props.stagger,
scrollTrigger: {
trigger: el,
scroller,
start: props.scrollStart,
end: props.scrollEnd,
scrub: true
}
}
); );
</script>
<style scoped> scrollTriggerInstance = tl.scrollTrigger || null;
.scroll-float { };
overflow: hidden;
}
.scroll-float-text { onMounted(() => {
display: inline-block; initializeAnimation();
font-size: clamp(1.6rem, 8vw, 10rem); });
font-weight: 900;
text-align: center;
line-height: 1.5;
}
.char { onUnmounted(() => {
display: inline-block; if (scrollTriggerInstance) {
scrollTriggerInstance.kill();
} }
</style> });
watch(
[
() => props.children,
() => props.scrollContainerRef,
() => props.animationDuration,
() => props.ease,
() => props.scrollStart,
() => props.scrollEnd,
() => props.stagger
],
() => {
initializeAnimation();
},
{ deep: true }
);
</script>

View File

@@ -1,5 +1,4 @@
<template> <template>
<div class="profile-card-demo">
<TabbedLayout> <TabbedLayout>
<template #preview> <template #preview>
<div <div
@@ -47,7 +46,6 @@
<CliInstallation :command="scrollFloatCode.cli" /> <CliInstallation :command="scrollFloatCode.cli" />
</template> </template>
</TabbedLayout> </TabbedLayout>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">