251 lines
5.1 KiB
Vue
251 lines
5.1 KiB
Vue
<script setup>
|
|
import { ref, onMounted, onUnmounted } from "vue";
|
|
import gsap from "gsap";
|
|
import ScrollTrigger from "gsap/ScrollTrigger";
|
|
import Flip from "gsap/dist/Flip";
|
|
import { checkCompatEnabled } from "@vue/compiler-core";
|
|
|
|
gsap.registerPlugin(Flip, ScrollTrigger);
|
|
|
|
let flipCtx;
|
|
|
|
const createTimeline = () => {
|
|
flipCtx && flipCtx.revert(); // alte Flip-Timeline zurücksetzen
|
|
|
|
flipCtx = gsap.context(() => {
|
|
const flipConfig = { ease: "none", duration: 1 };
|
|
|
|
// Flip-States der Marker holen
|
|
const secondState = Flip.getState(".second .marker");
|
|
const thirdState = Flip.getState(".third .marker");
|
|
|
|
// Timeline mit ScrollTrigger
|
|
const tl = gsap.timeline({
|
|
scrollTrigger: {
|
|
trigger: ".container.initial",
|
|
start: "clamp(top center)",
|
|
endTrigger: ".final",
|
|
end: "clamp(bottom center)",
|
|
scrub: 1,
|
|
},
|
|
});
|
|
|
|
// initial -> second
|
|
tl.add(Flip.fit(".circle", secondState, flipConfig)).add(() => {
|
|
gsap.set(".second-btn", {
|
|
opacity: 1,
|
|
pointerEvents: "auto",
|
|
zIndex: 1000,
|
|
});
|
|
}, "+=0.1");
|
|
|
|
// second -> third
|
|
tl.add(Flip.fit(".circle", thirdState, flipConfig)).add(() => {
|
|
gsap.set(".third-btn", { opacity: 1, pointerEvents: "auto" });
|
|
}, "+=0.01");
|
|
});
|
|
};
|
|
|
|
onMounted(() => {
|
|
createTimeline();
|
|
window.addEventListener("resize", createTimeline);
|
|
ScrollTrigger.refresh();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
flipCtx?.revert();
|
|
window.removeEventListener("resize", createTimeline);
|
|
});
|
|
|
|
window.addEventListener("resize", createTimeline);
|
|
</script>
|
|
|
|
<template>
|
|
<section class="text-section">
|
|
<div class="main">
|
|
<div class="container initial">
|
|
<div class="circle"></div>
|
|
</div>
|
|
|
|
<div class="container second">
|
|
<div class="experience-text">
|
|
<h2>Enten?</h2>
|
|
</div>
|
|
<div class="marker"></div>
|
|
<router-link to="/books">
|
|
<button class="btn second-btn">Books i've read</button>
|
|
</router-link>
|
|
</div>
|
|
<div class="container third">
|
|
<div class="experience-text">
|
|
<h2>Enten?</h2>
|
|
</div>
|
|
<div class="marker"></div>
|
|
<router-link to="/ducks">
|
|
<button class="btn third-btn">zu den Enten</button>
|
|
</router-link>
|
|
</div>
|
|
|
|
<div class="container fourth">
|
|
<div class="experience-text">
|
|
<h2>Here is the final step!</h2>
|
|
</div>
|
|
<div class="marker"></div>
|
|
<button class="btn fourth-btn">Next</button>
|
|
</div>
|
|
</div>
|
|
<div class="spacer final"></div>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
h2 {
|
|
font-size: 1.2rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.highlight {
|
|
background: linear-gradient(90deg, #ffffff, #ffffff);
|
|
background-repeat: no-repeat;
|
|
background-size: 0% 100%;
|
|
background-position: left;
|
|
padding: 0 0.3rem;
|
|
}
|
|
|
|
.btn {
|
|
background-color: white;
|
|
color: #1a1a2e; /* dunkler Text */
|
|
border: none;
|
|
padding: 0.6rem 1.5rem;
|
|
font-size: 1rem;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
|
opacity: 0;
|
|
}
|
|
|
|
.btn:hover {
|
|
background-color: #f0f0f0;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.btn:active {
|
|
transform: translateY(0);
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
body {
|
|
background-image:
|
|
linear-gradient(rgba(255, 255, 255, 0.05) 2px, transparent 2px),
|
|
linear-gradient(90deg, rgba(255, 255, 255, 0.05) 2px, transparent 2px),
|
|
linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px);
|
|
background-size:
|
|
100px 100px,
|
|
100px 100px,
|
|
20px 20px,
|
|
20px 20px;
|
|
background-position:
|
|
-2px -2px,
|
|
-2px -2px,
|
|
-1px -1px,
|
|
-1px -1px;
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.spacer {
|
|
width: 100%;
|
|
height: 20vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.main {
|
|
position: relative;
|
|
height: 200vh;
|
|
min-height: 800px;
|
|
--grid-line: rgba(255, 255, 255, 0.08);
|
|
--box-border: rgba(255, 255, 255, 0.25);
|
|
}
|
|
|
|
.container {
|
|
position: absolute;
|
|
width: 200px;
|
|
height: 200px;
|
|
display: flex;
|
|
flex-direction: column; /* Items untereinander */
|
|
align-items: center; /* horizontal zentrieren */
|
|
justify-content: center; /* vertikal zentrieren, kann auch flex-start sein */
|
|
}
|
|
|
|
.experience-text {
|
|
width: 200px;
|
|
text-align: center;
|
|
}
|
|
|
|
.experience-text h2 {
|
|
font-size: 1.2rem;
|
|
color: white;
|
|
margin: 0;
|
|
}
|
|
|
|
.initial {
|
|
left: 60%;
|
|
top: 0%;
|
|
}
|
|
|
|
.circle {
|
|
width: 100px;
|
|
height: 100px;
|
|
position: relative;
|
|
z-index: 10;
|
|
border-radius: 10px;
|
|
background-size: contain;
|
|
background-repeat: no-repeat;
|
|
background-color: transparent;
|
|
background-image: url("../../assets/bird-claw.svg");
|
|
}
|
|
|
|
.second {
|
|
left: 10%;
|
|
top: 50%;
|
|
}
|
|
|
|
.marker {
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.second .marker {
|
|
width: 100px;
|
|
height: 100px;
|
|
}
|
|
|
|
.third {
|
|
right: 10%;
|
|
bottom: 3rem;
|
|
}
|
|
|
|
.third .marker {
|
|
width: 100px;
|
|
height: 100px;
|
|
}
|
|
|
|
.fourth {
|
|
left: 10%;
|
|
top: 750%;
|
|
}
|
|
|
|
.fourth .marker {
|
|
width: 100px;
|
|
height: 100px;
|
|
}
|
|
</style>
|