133 lines
2.4 KiB
Vue
133 lines
2.4 KiB
Vue
<script setup lang="ts">
|
|
import Main from "../components/slim/Main.vue";
|
|
import AboutMe from "../components/slim/AboutMe.vue";
|
|
import Goals from "../components/slim/Goals.vue";
|
|
|
|
import { onMounted, onUnmounted, nextTick, ref } from "vue";
|
|
|
|
import gsap from "gsap";
|
|
import ScrollTrigger from "gsap/ScrollTrigger";
|
|
import ScrollSmoother from "gsap/ScrollSmoother";
|
|
import { S } from "vue-router/dist/router-CWoNjPRp.mjs";
|
|
|
|
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
|
|
|
|
let smoother: ScrollSmoother | null = null;
|
|
|
|
const transitioning = ref(false);
|
|
|
|
onMounted(async () => {
|
|
await nextTick();
|
|
|
|
if (smoother) return;
|
|
|
|
smoother = ScrollSmoother.create({
|
|
wrapper: "#smooth-wrapper",
|
|
content: "#smooth-content",
|
|
smooth: 1.8,
|
|
effects: true,
|
|
});
|
|
});
|
|
const scrollTo = (target: string) => {
|
|
if (!smoother) return;
|
|
|
|
smoother.scrollTo(target, true);
|
|
};
|
|
|
|
const morphTo = (target: string) => {
|
|
if (transitioning.value) return;
|
|
|
|
const current =
|
|
target === "#goals"
|
|
? document.querySelector("#me")
|
|
: document.querySelector("#goals");
|
|
|
|
const next = document.querySelector(target);
|
|
|
|
if (!current || !next) return;
|
|
|
|
transitioning.value = true;
|
|
|
|
gsap.set(next, {
|
|
opacity: 0,
|
|
scale: 0.85,
|
|
filter: "blur(20px)",
|
|
clipPath: "circle(0% at 50% 50%)",
|
|
pointerEvents: "none",
|
|
});
|
|
|
|
gsap.set(current, {
|
|
transformOrigin: "50% 50%",
|
|
});
|
|
|
|
const tl = gsap.timeline({
|
|
onComplete: () => {
|
|
transitioning.value = false;
|
|
scrollTo(target);
|
|
},
|
|
});
|
|
|
|
tl.to(current, {
|
|
scale: 0.92,
|
|
opacity: 0,
|
|
filter: "blur(15px)",
|
|
duration: 0.4,
|
|
ease: "power1.in",
|
|
})
|
|
|
|
.set(next, {
|
|
opacity: 1,
|
|
pointerEvents: "auto",
|
|
})
|
|
|
|
.to(next, {
|
|
scale: 1,
|
|
filter: "blur(0px)",
|
|
clipPath: "circle(150% at 50% 50%)",
|
|
duration: 0.8,
|
|
ease: "power4.inOut",
|
|
});
|
|
};
|
|
|
|
onUnmounted(() => {
|
|
if (smoother) {
|
|
smoother.kill();
|
|
smoother = null;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div id="smooth-wrapper">
|
|
<div id="smooth-content">
|
|
<Main id="welcome" @scroll-to-next="scrollTo" />
|
|
|
|
<section class="component-wrapper">
|
|
<AboutMe id="me" @morph-to="morphTo" />
|
|
|
|
<Goals id="goals" @morph-to="morphTo" />
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.component-wrapper {
|
|
position: relative;
|
|
width: 100%;
|
|
}
|
|
|
|
#me,
|
|
#goals {
|
|
width: 100%;
|
|
}
|
|
|
|
#goals {
|
|
position: absolute;
|
|
inset: 0;
|
|
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
</style>
|