189 lines
3.6 KiB
Vue
189 lines
3.6 KiB
Vue
<script setup lang="js">
|
|
import { onMounted, onUnmounted, ref } from "vue"
|
|
import gsap from "gsap"
|
|
|
|
import ScrollTrigger from "gsap/ScrollTrigger"
|
|
gsap.registerPlugin(ScrollTrigger)
|
|
|
|
|
|
|
|
const main = ref(null);
|
|
let tl;
|
|
let ctx;
|
|
|
|
|
|
function setBoxes() {
|
|
|
|
const wrapper = main.value.querySelector('.content-wrapper');
|
|
const rect = wrapper.getBoundingClientRect();
|
|
|
|
const boxes = gsap.utils.toArray(".box");
|
|
|
|
gsap.set(".title", { y: 200 });
|
|
|
|
boxes.forEach((box) => {
|
|
|
|
const randomX = gsap.utils.random(0, rect.width);
|
|
const randomY = gsap.utils.random(0, rect.height);
|
|
|
|
gsap.set(box, {
|
|
x: randomX,
|
|
y: randomY,
|
|
xPercent: -50,
|
|
yPercent: -50
|
|
});
|
|
|
|
});
|
|
}
|
|
|
|
function toggleTimeline() {
|
|
tl.reversed(!tl.reversed())
|
|
}
|
|
function startNextAnimation() {
|
|
const centerX = window.innerWidth / 2
|
|
const centerY = window.innerHeight / 2
|
|
const radius = 250
|
|
|
|
ctx = gsap.context(() => {
|
|
const boxes = gsap.utils.toArray(".box")
|
|
tl = gsap.timeline({
|
|
scrollTrigger: {
|
|
|
|
start: "top top",
|
|
end: "+=100%",
|
|
scrub: true,
|
|
pin: true,
|
|
}
|
|
})
|
|
|
|
tl.to(boxes, {
|
|
y: () => gsap.utils.random(-400, -150),
|
|
rotate: () => gsap.utils.random(-180, 180),
|
|
ease: "none",
|
|
stagger: 0.05
|
|
}).to(".title", {
|
|
y: window.innerHeight - 130,
|
|
ease: "power2.out",
|
|
duration: 1.0
|
|
}, 0)
|
|
|
|
}, main.value)
|
|
}
|
|
|
|
onMounted(() => {
|
|
setBoxes();
|
|
startNextAnimation();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
ctx?.revert()
|
|
// scrollTrigger.getAll().forEach(t => t.kill())
|
|
})
|
|
|
|
|
|
</script>
|
|
<template>
|
|
<section class="container" ref="main">
|
|
<div class="content-wrapper">
|
|
<h1 class="title">Welcome</h1>
|
|
<div class="box">l</div>
|
|
<div class="box">e</div>
|
|
<div class="box">e</div>
|
|
<div class="box">r</div>
|
|
<div class="box">p</div>
|
|
<div class="box"></div>
|
|
<div class="box">H</div>
|
|
<div class="box">e</div>
|
|
<div class="box">d</div>
|
|
<div class="box">n</div>
|
|
<div class="box">a</div>
|
|
<div class="box">e</div>
|
|
<div class="box">l</div>
|
|
<div class="box">A</div>
|
|
<div class="box">x</div>
|
|
<div class="box">m</div>
|
|
<div class="box">o</div>
|
|
<div class="box">T</div>
|
|
<div class="box">r</div>
|
|
<div class="box">l</div>
|
|
<div class="box">e</div>
|
|
<div class="box">e</div>
|
|
<div class="box">r</div>
|
|
<div class="box">p</div>
|
|
<div class="box"></div>
|
|
<div class="box">H</div>
|
|
<div class="box">e</div>
|
|
<div class="box">d</div>
|
|
<div class="box">n</div>
|
|
<div class="box">a</div>
|
|
<div class="box">e</div>
|
|
<div class="box">l</div>
|
|
<div class="box">A</div>
|
|
<div class="box">x</div>
|
|
<div class="box">m</div>
|
|
<div class="box">o</div>
|
|
<div class="box">T</div>
|
|
<div class="box">r</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
|
|
.container {
|
|
background: radial-gradient(
|
|
129% 99% at 20% 85%,
|
|
rgb(54, 54, 54) 20%,
|
|
rgb(0, 0, 0) 90%
|
|
);
|
|
background-blend-mode: color-dodge;
|
|
min-height: 100vh;
|
|
color: white;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
|
|
.content-wrapper {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 80vh;
|
|
|
|
}
|
|
|
|
.title {
|
|
position: relative;
|
|
z-index: 1;
|
|
text-align: center;
|
|
|
|
font-size: clamp(2rem, 12rem, 5vw);
|
|
margin: 0;
|
|
}
|
|
|
|
|
|
|
|
.box {
|
|
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
font-weight: 600;
|
|
color: var(--green);
|
|
|
|
line-height: 1.2;
|
|
will-change: transform;
|
|
position: absolute;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
</style> |