This commit is contained in:
2026-07-10 18:23:41 +08:00
commit bc9e794a57
80 changed files with 19016 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
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();
});
}
+42
View File
@@ -0,0 +1,42 @@
import { onBeforeUnmount, onMounted, unref } from "vue";
export function usePageShellMotion(rootRef) {
let ctx;
onMounted(() => {
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
return;
}
const root = unref(rootRef);
const { $gsap } = useNuxtApp();
if (!root || !$gsap) {
return;
}
ctx = $gsap.context(() => {
$gsap.fromTo(
root,
{ autoAlpha: 0 },
{ autoAlpha: 1, duration: 0.42, ease: "power2.out" },
);
root.querySelectorAll("[data-cta-orbit]").forEach((element) => {
$gsap.to(element, {
scale: 1.08,
autoAlpha: 0.72,
duration: 2.8,
ease: "sine.inOut",
repeat: -1,
yoyo: true,
});
});
}, root);
});
onBeforeUnmount(() => {
ctx?.revert();
});
}