198 lines
4.6 KiB
JavaScript
198 lines
4.6 KiB
JavaScript
|
|
import { onBeforeUnmount, onMounted, unref } from "vue";
|
||
|
|
|
||
|
|
export function useGsapReveal(rootRef) {
|
||
|
|
let ctx;
|
||
|
|
const cleanup = [];
|
||
|
|
|
||
|
|
onMounted(() => {
|
||
|
|
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const root = unref(rootRef);
|
||
|
|
const { $gsap } = useNuxtApp();
|
||
|
|
|
||
|
|
if (!root || !$gsap) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx = $gsap.context(() => {
|
||
|
|
const isTouch = window.matchMedia("(hover: none), (pointer: coarse)").matches;
|
||
|
|
const heroItems = root.querySelectorAll("[data-hero-reveal]");
|
||
|
|
|
||
|
|
if (heroItems.length) {
|
||
|
|
$gsap.fromTo(
|
||
|
|
heroItems,
|
||
|
|
{
|
||
|
|
autoAlpha: 0,
|
||
|
|
y: 34,
|
||
|
|
filter: "blur(10px)",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
autoAlpha: 1,
|
||
|
|
y: 0,
|
||
|
|
filter: "blur(0px)",
|
||
|
|
duration: 0.95,
|
||
|
|
stagger: 0.12,
|
||
|
|
ease: "power3.out",
|
||
|
|
},
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
root.querySelectorAll("[data-reveal]").forEach((element, index) => {
|
||
|
|
$gsap.fromTo(
|
||
|
|
element,
|
||
|
|
{
|
||
|
|
autoAlpha: 0,
|
||
|
|
y: 26,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
autoAlpha: 1,
|
||
|
|
y: 0,
|
||
|
|
duration: 0.72,
|
||
|
|
delay: index * 0.025,
|
||
|
|
ease: "power2.out",
|
||
|
|
scrollTrigger: {
|
||
|
|
trigger: element,
|
||
|
|
start: "top 85%",
|
||
|
|
once: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
root.querySelectorAll("[data-stagger]").forEach((element) => {
|
||
|
|
const children = element.querySelectorAll("[data-stagger-item]");
|
||
|
|
|
||
|
|
if (!children.length) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$gsap.fromTo(
|
||
|
|
children,
|
||
|
|
{
|
||
|
|
autoAlpha: 0,
|
||
|
|
y: 22,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
autoAlpha: 1,
|
||
|
|
y: 0,
|
||
|
|
duration: 0.64,
|
||
|
|
stagger: 0.09,
|
||
|
|
ease: "power2.out",
|
||
|
|
scrollTrigger: {
|
||
|
|
trigger: element,
|
||
|
|
start: "top 82%",
|
||
|
|
once: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
root.querySelectorAll("[data-parallax]").forEach((element) => {
|
||
|
|
const distance = Number(element.dataset.parallax) || 42;
|
||
|
|
|
||
|
|
$gsap.fromTo(
|
||
|
|
element,
|
||
|
|
{ y: distance * -0.35 },
|
||
|
|
{
|
||
|
|
y: distance,
|
||
|
|
ease: "none",
|
||
|
|
scrollTrigger: {
|
||
|
|
trigger: element,
|
||
|
|
start: "top bottom",
|
||
|
|
end: "bottom top",
|
||
|
|
scrub: 0.8,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
root.querySelectorAll("[data-geo-loop]").forEach((element) => {
|
||
|
|
const nodes = element.querySelectorAll("[data-geo-node]");
|
||
|
|
|
||
|
|
if (!nodes.length) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$gsap.fromTo(
|
||
|
|
nodes,
|
||
|
|
{ autoAlpha: 0.48, x: -10 },
|
||
|
|
{
|
||
|
|
autoAlpha: 1,
|
||
|
|
x: 0,
|
||
|
|
duration: 0.45,
|
||
|
|
stagger: 0.11,
|
||
|
|
ease: "power2.out",
|
||
|
|
scrollTrigger: {
|
||
|
|
trigger: element,
|
||
|
|
start: "top 75%",
|
||
|
|
once: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
root.querySelectorAll("[data-flow-line]").forEach((element) => {
|
||
|
|
$gsap.fromTo(
|
||
|
|
element,
|
||
|
|
{ width: "0%" },
|
||
|
|
{
|
||
|
|
width: "100%",
|
||
|
|
duration: 1,
|
||
|
|
ease: "power2.out",
|
||
|
|
scrollTrigger: {
|
||
|
|
trigger: element,
|
||
|
|
start: "top 82%",
|
||
|
|
once: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
if (isTouch) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
root.querySelectorAll("[data-hover-card]").forEach((element) => {
|
||
|
|
const onMove = (event) => {
|
||
|
|
const rect = element.getBoundingClientRect();
|
||
|
|
const x = ((event.clientX - rect.left) / rect.width - 0.5) * 10;
|
||
|
|
const y = ((event.clientY - rect.top) / rect.height - 0.5) * -10;
|
||
|
|
|
||
|
|
$gsap.to(element, {
|
||
|
|
rotateX: y,
|
||
|
|
rotateY: x,
|
||
|
|
y: -6,
|
||
|
|
duration: 0.35,
|
||
|
|
ease: "power2.out",
|
||
|
|
transformPerspective: 900,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
const onLeave = () => {
|
||
|
|
$gsap.to(element, {
|
||
|
|
rotateX: 0,
|
||
|
|
rotateY: 0,
|
||
|
|
y: 0,
|
||
|
|
duration: 0.42,
|
||
|
|
ease: "power2.out",
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
element.addEventListener("mousemove", onMove);
|
||
|
|
element.addEventListener("mouseleave", onLeave);
|
||
|
|
cleanup.push(() => {
|
||
|
|
element.removeEventListener("mousemove", onMove);
|
||
|
|
element.removeEventListener("mouseleave", onLeave);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}, root);
|
||
|
|
});
|
||
|
|
|
||
|
|
onBeforeUnmount(() => {
|
||
|
|
cleanup.forEach((dispose) => dispose());
|
||
|
|
ctx?.revert();
|
||
|
|
});
|
||
|
|
}
|