morph
This commit is contained in:
@@ -74,13 +74,26 @@
|
|||||||
<span class="number">03 / 04</span>
|
<span class="number">03 / 04</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="interests">
|
||||||
<span class="label">Based in</span>
|
<span class="label">Misc</span>
|
||||||
|
|
||||||
<p class="small location">
|
<ul class="interessen-list">
|
||||||
Germany<br />
|
<li
|
||||||
Europe
|
v-for="(interest, index) in interests"
|
||||||
</p>
|
:key="interest.name"
|
||||||
|
@click="emit('morphTo', interest.target)"
|
||||||
|
>
|
||||||
|
<span class="number">
|
||||||
|
{{ String(index + 1).padStart(2, "0") }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="interest-name">
|
||||||
|
{{ interest.name }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="arrow">↗</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -99,8 +112,35 @@
|
|||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import tom_img from "../../assets/myself/SmartTom.jpeg";
|
import tom_img from "../../assets/myself/SmartTom.jpeg";
|
||||||
|
|
||||||
|
const interests = [
|
||||||
|
{
|
||||||
|
name: "Goals",
|
||||||
|
target: "#goals",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Coding",
|
||||||
|
target: "#coding",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Videos",
|
||||||
|
target: "#videos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Books",
|
||||||
|
target: "#books",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Experiences",
|
||||||
|
target: "#experiences",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
morphTo: [target: string];
|
||||||
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -317,10 +357,16 @@ import tom_img from "../../assets/myself/SmartTom.jpeg";
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
}
|
}
|
||||||
|
.interests {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
font-size: 11px;
|
display: block;
|
||||||
font-weight: 700;
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
}
|
}
|
||||||
@@ -357,16 +403,63 @@ import tom_img from "../../assets/myself/SmartTom.jpeg";
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skill-list li {
|
.skill-list li,
|
||||||
|
.interressen-list li {
|
||||||
padding: 6px 0;
|
padding: 6px 0;
|
||||||
|
|
||||||
border-top: 1px solid rgba(41, 37, 31, 0.35);
|
border-top: 1px solid rgba(41, 37, 31, 0.35);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
.skill-list li:last-child {
|
.skill-list li:last-child,
|
||||||
|
.interressen-list li:last-child {
|
||||||
border-bottom: 1px solid rgba(41, 37, 31, 0.35);
|
border-bottom: 1px solid rgba(41, 37, 31, 0.35);
|
||||||
|
} */
|
||||||
|
|
||||||
|
.interessen-list li {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50px 1fr 30px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
min-height: 70px;
|
||||||
|
|
||||||
|
border-top: 1px solid rgba(31, 31, 31, 0.35);
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition:
|
||||||
|
padding-left 0.4s cubic-bezier(0.22, 1, 0.36, 1),
|
||||||
|
background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.interessen-list li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interest-name {
|
||||||
|
font-size: clamp(24px, 3vw, 42px);
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
font-size: 24px;
|
||||||
|
justify-self: end;
|
||||||
|
|
||||||
|
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.interessen-list li:hover {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interessen-list li:hover .arrow {
|
||||||
|
transform: translateX(8px) rotate(45deg);
|
||||||
|
}
|
||||||
.footer-links {
|
.footer-links {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 25px;
|
gap: 25px;
|
||||||
|
|||||||
11
src/components/slim/Goals.vue
Normal file
11
src/components/slim/Goals.vue
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<h2 @click="emit('morphTo', '#me')">Hallo</h2>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const emit = defineEmits<{
|
||||||
|
morphTo: [target: string];
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<button class="div14 nav-item">
|
<button class="div14 nav-item" @click="emit('scrollToNext', '#me')">
|
||||||
<span>01</span>
|
<span>01</span>
|
||||||
<span>About</span>
|
<span>About Me</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="div15 nav-item">
|
<button class="div15 nav-item">
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="div21 small-block">
|
<section class="div21 small-block" @click="emit('scrollToNext', '#me')">
|
||||||
<span>03</span>
|
<span>03</span>
|
||||||
<span>Scroll</span>
|
<span>Scroll</span>
|
||||||
<span class="arrow">↓</span>
|
<span class="arrow">↓</span>
|
||||||
@@ -106,8 +106,11 @@
|
|||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
const emit = defineEmits<{
|
||||||
|
scrollToNext: [target: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
const isInteractiveHovered = ref(false);
|
const isInteractiveHovered = ref(false);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,42 +1,132 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Main from "../components/slim/Main.vue";
|
import Main from "../components/slim/Main.vue";
|
||||||
import AboutMe from "../components/slim/AboutMe.vue";
|
import AboutMe from "../components/slim/AboutMe.vue";
|
||||||
|
import Goals from "../components/slim/Goals.vue";
|
||||||
|
|
||||||
|
import { onMounted, onUnmounted, nextTick, ref } from "vue";
|
||||||
|
|
||||||
import { onMounted, onUnmounted, nextTick } from "vue";
|
|
||||||
import gsap from "gsap";
|
import gsap from "gsap";
|
||||||
import ScrollTrigger from "gsap/ScrollTrigger";
|
import ScrollTrigger from "gsap/ScrollTrigger";
|
||||||
import ScrollSmoother from "gsap/ScrollSmoother";
|
import ScrollSmoother from "gsap/ScrollSmoother";
|
||||||
|
import { S } from "vue-router/dist/router-CWoNjPRp.mjs";
|
||||||
|
|
||||||
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
|
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
|
||||||
|
|
||||||
let smoother: ScrollSmoother | null = null;
|
let smoother: ScrollSmoother | null = null;
|
||||||
|
|
||||||
|
const transitioning = ref(false);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
|
if (smoother) return;
|
||||||
|
|
||||||
smoother = ScrollSmoother.create({
|
smoother = ScrollSmoother.create({
|
||||||
wrapper: "#smooth-wrapper",
|
wrapper: "#smooth-wrapper",
|
||||||
content: "#smooth-content",
|
content: "#smooth-content",
|
||||||
smooth: 1.5,
|
smooth: 1.8,
|
||||||
effects: true,
|
effects: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
ScrollTrigger.refresh();
|
|
||||||
});
|
});
|
||||||
|
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(() => {
|
onUnmounted(() => {
|
||||||
smoother?.kill();
|
if (smoother) {
|
||||||
|
smoother.kill();
|
||||||
smoother = null;
|
smoother = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="smooth-wrapper">
|
<div id="smooth-wrapper">
|
||||||
<div id="smooth-content">
|
<div id="smooth-content">
|
||||||
<Main id="welcome" />
|
<Main id="welcome" @scroll-to-next="scrollTo" />
|
||||||
<AboutMe id="me" />
|
|
||||||
|
<section class="component-wrapper">
|
||||||
|
<AboutMe id="me" @morph-to="morphTo" />
|
||||||
|
|
||||||
|
<Goals id="goals" @morph-to="morphTo" />
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="css" scoped></style>
|
<style scoped>
|
||||||
|
.component-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#me,
|
||||||
|
#goals {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#goals {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user