perf(movimento): sem tela vazia, passagens em CSS e vitrine leve
Projetos sobem sobre o palco do hero e o celular tem hero de uma tela sem mergulho; passagens com animation-timeline em CSS, sem GSAP nem motion; troca da vitrine sem texto duplicado; moldura de celular só com pôster e scrub reencodado em 960 px (9,4 para 5,2 MB); 3D monta na primeira interação e para quando parado; transição entre páginas do mesmo segmento; foco em anel duplo; texto de 15 px no celular; código morto removido.
This commit is contained in:
1 parent
10f170abf4
commit
318a764190
77 files changed
+4293
-4951
No files matched your search
@@ -1,9 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "motion/react";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import { useState } from "react";
|
||||
import { useReducedMotion } from "@/app/hooks/useReducedMotion";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
interface LazyRevealImageProps {
|
||||
src: string | StaticImageData;
|
||||
@@ -13,6 +11,7 @@ interface LazyRevealImageProps {
|
||||
sizes?: string;
|
||||
}
|
||||
|
||||
/** Imagem que entra com um fade curto quando termina de carregar (CSS em globals.css, .ds-lazy-reveal). */
|
||||
export function LazyRevealImage({
|
||||
src,
|
||||
alt,
|
||||
@@ -20,31 +19,20 @@ export function LazyRevealImage({
|
||||
imageClassName = "object-cover",
|
||||
sizes = "(max-width: 1024px) 100vw, 50vw",
|
||||
}: Readonly<LazyRevealImageProps>) {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
// uma imagem que já estava no cache termina antes de a página hidratar: o onLoad não vem mais
|
||||
useEffect(() => {
|
||||
const image = ref.current?.querySelector("img");
|
||||
if (image?.complete && image.naturalWidth > 0) setLoaded(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className={`relative ${className}`.trim()}
|
||||
initial={
|
||||
shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: 20, scale: 1.02 }
|
||||
}
|
||||
animate={
|
||||
loaded
|
||||
? shouldReduceMotion
|
||||
? { opacity: 1 }
|
||||
: { opacity: 1, y: 0, scale: 1 }
|
||||
: shouldReduceMotion
|
||||
? { opacity: 0 }
|
||||
: { opacity: 0, y: 20, scale: 1.02 }
|
||||
}
|
||||
transition={{
|
||||
duration: shouldReduceMotion ? 0.25 : 0.75,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
}}
|
||||
style={{
|
||||
willChange: !shouldReduceMotion ? "transform, opacity" : undefined,
|
||||
}}
|
||||
<div
|
||||
ref={ref}
|
||||
className={`ds-lazy-reveal relative ${className}`.trim()}
|
||||
data-loaded={loaded ? "" : undefined}
|
||||
>
|
||||
<Image
|
||||
src={src}
|
||||
@@ -56,6 +44,6 @@ export function LazyRevealImage({
|
||||
onLoad={() => setLoaded(true)}
|
||||
className={imageClassName}
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user