78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
"use client";
|
||
|
||
import { type ReactNode } from "react";
|
||
import { ReactLenis } from "lenis/react";
|
||
import {
|
||
motion,
|
||
useReducedMotion,
|
||
useScroll,
|
||
useSpring,
|
||
useTransform,
|
||
useVelocity,
|
||
} from "motion/react";
|
||
|
||
/**
|
||
* Vitrin sayfalarının akışkan scroll efekti: Lenis ile lerp'li (yavaşlatılmış,
|
||
* momentumlu) kaydırma + scroll hızına bağlı içerik bükülmesi (skew).
|
||
* Hızlı kaydırdıkça sayfa hafifçe yana yatar, durunca yayla yerine oturur.
|
||
*
|
||
* Sohbet ve liste gibi iç scroll alanı olan uygulama sayfalarında KULLANMA —
|
||
* Lenis tekerlek olayını sayfaya yönlendirdiği için iç scroll'u bozar,
|
||
* transform da `fixed` konumlu çocukları koparır.
|
||
*
|
||
* Hareket azaltılmışsa hem yumuşatma hem bükülme tamamen kapalıdır.
|
||
*/
|
||
export function ScrollFlow({
|
||
children,
|
||
className,
|
||
maxSkew = 3,
|
||
}: {
|
||
children: ReactNode;
|
||
className?: string;
|
||
/** Derece cinsinden en fazla bükülme açısı. */
|
||
maxSkew?: number;
|
||
}) {
|
||
const reduceMotion = useReducedMotion();
|
||
const { scrollY } = useScroll();
|
||
const velocity = useVelocity(scrollY);
|
||
|
||
// Önce hızı kesintisiz bir hedef açıya dönüştür, sonra doğrudan görünen
|
||
// açıyı yumuşat. Böylece eşik/clamp kaynaklı ani hız değişimleri yaya girmez.
|
||
const targetSkew = useTransform(velocity, (v) => {
|
||
const RESPONSE = 1800;
|
||
return Math.tanh(v / RESPONSE) * maxSkew;
|
||
});
|
||
const smoothSkew = useSpring(targetSkew, {
|
||
stiffness: 240,
|
||
damping: 26,
|
||
mass: 0.7,
|
||
restDelta: 0.005,
|
||
restSpeed: 0.01,
|
||
});
|
||
|
||
// Yuvarlama görsel basamaklanma üretir. Tam hassasiyetle çiz; yalnızca açı
|
||
// algılanamayacak kadar küçüldüğünde transform'u kaldırarak dev sayfa
|
||
// katmanının GPU belleğinde boşuna tutulmasını önle.
|
||
const transform = useTransform(smoothSkew, (v) => {
|
||
if (Math.abs(v) < 0.01) return "none";
|
||
return `skewY(${v}deg)`;
|
||
});
|
||
|
||
// Hareket azaltılmışsa da AYNI ağaç render edilir (server/client farklı
|
||
// ağaç hydration uyumsuzluğu yaratıyordu); efektler koşullu kapatılır:
|
||
// Lenis tekerlek yumuşatması kapalı, skew transform'u hiç bağlanmaz.
|
||
return (
|
||
<ReactLenis
|
||
root
|
||
options={{ lerp: 0.09, anchors: true, smoothWheel: !reduceMotion }}
|
||
>
|
||
<motion.div
|
||
className={className}
|
||
style={reduceMotion ? undefined : { transform }}
|
||
>
|
||
{children}
|
||
</motion.div>
|
||
</ReactLenis>
|
||
);
|
||
}
|