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:
Evandro Fonseca Junior committed 2026-10-14 14:15:50 -03:00
1 parent 10f170abf4
commit 318a764190
77 files changed
+4293 -4951

No files matched your search

+20 -44
View File
@@ -1,22 +1,21 @@
"use client";
import { motion } from "motion/react";
import type { ReactNode } from "react";
import { useState } from "react";
import { useReducedMotion } from "@/app/hooks/useReducedMotion";
import { type CSSProperties, type ReactNode, useRef } from "react";
import { useInView } from "@/app/hooks/useInView";
interface RevealProps {
children: ReactNode;
y?: number;
duration?: number;
delay?: number;
ease?: string;
start?: string;
end?: string;
scrub?: boolean | number;
className?: string;
}
/**
* Conteúdo que sobe e aparece uma vez, quando chega à janela. A animação é CSS (.ds-reveal em globals.css):
* só opacidade e deslocamento, que a placa de vídeo compõe sem redesenhar. Sem JavaScript ou com movimento
* reduzido, o conteúdo já nasce visível.
*/
export function Reveal({
children,
y = 36,
@@ -24,46 +23,23 @@ export function Reveal({
delay = 0,
className = "",
}: RevealProps) {
const shouldReduceMotion = useReducedMotion();
const [animating, setAnimating] = useState(!shouldReduceMotion);
const ref = useRef<HTMLDivElement>(null);
const shown = useInView(ref);
return (
<motion.div
className={className}
initial={
shouldReduceMotion
? { opacity: 0 }
: { opacity: 0, y, clipPath: "inset(100% 0 0 0)" }
<div
ref={ref}
className={`ds-reveal ${className}`.trim()}
data-shown={shown ? "" : undefined}
style={
{
"--reveal-y": `${y}px`,
"--reveal-duration": `${duration}s`,
"--reveal-delay": `${delay}s`,
} as CSSProperties
}
whileInView={
shouldReduceMotion
? { opacity: 1 }
: { opacity: 1, y: 0, clipPath: "inset(0% 0 0 0)" }
}
viewport={{ once: true, amount: 0.2 }}
transition={
shouldReduceMotion
? { duration: 0.25 }
: {
duration,
delay,
ease: [0.16, 1, 0.3, 1],
}
}
style={{
willChange:
animating && !shouldReduceMotion
? "transform, opacity, clip-path"
: undefined,
}}
onAnimationStart={() => {
if (!shouldReduceMotion) {
setAnimating(true);
}
}}
onAnimationComplete={() => setAnimating(false)}
>
{children}
</motion.div>
</div>
);
}