perf: Lenis smooth scroll ve scroll skew efektini kaldır
Site geneli ScrollFlow sarmalayıcısı (Lenis lerp'li kaydırma + hıza bağlı skewY) tüm sayfa katmanını her karede transform'luyor, kaydırma performansını düşürüyordu. Yerel tarayıcı kaydırmasına dönüldü; bölüm çapaları mevcut scroll-mt değerleriyle çalışmaya devam eder. lenis bağımlılığı ve data-lenis-prevent işaretleri temizlendi. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -178,7 +178,6 @@ export function DevPanel() {
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-lenis-prevent
|
||||
className="flex flex-col gap-4 overflow-y-auto p-4 text-sm"
|
||||
>
|
||||
{/* Oturum */}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
@@ -26,7 +26,6 @@ function ChatContainerRoot({
|
||||
return (
|
||||
<StickToBottom
|
||||
className={cn("flex overflow-y-auto", className)}
|
||||
data-lenis-prevent
|
||||
resize="smooth"
|
||||
initial="instant"
|
||||
role="log"
|
||||
|
||||
@@ -60,7 +60,6 @@ function DialogContent({
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
data-lenis-prevent
|
||||
className={cn(
|
||||
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
||||
className
|
||||
|
||||
@@ -68,7 +68,6 @@ function SelectContent({
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
data-slot="select-content"
|
||||
data-lenis-prevent
|
||||
data-align-trigger={position === "item-aligned"}
|
||||
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
|
||||
position={position}
|
||||
|
||||
@@ -8,9 +8,9 @@ const getClientSnapshot = () => true;
|
||||
const getServerSnapshot = () => false;
|
||||
|
||||
/**
|
||||
* Viewport'a sabit kalması gereken öğeleri, transform uygulanan sayfa
|
||||
* katmanının dışına taşır. Böylece üst katmandaki scroll skew animasyonu
|
||||
* `position: fixed` koordinat sistemini değiştirmez.
|
||||
* Viewport'a sabit kalması gereken öğeleri sayfa katmanının dışına, doğrudan
|
||||
* body'ye taşır. Böylece üst katmanlardaki bir transform (ör. Parallax/Reveal)
|
||||
* `position: fixed` koordinat sistemini değiştiremez.
|
||||
*/
|
||||
export function ViewportPortal({ children }: { children: ReactNode }) {
|
||||
const mounted = useSyncExternalStore(
|
||||
|
||||
Reference in New Issue
Block a user