- Introduced DrawLine component for drawing animations. - Added LazyRevealImage component for lazy loading images with reveal animations. - Implemented TextDiffuseReveal for animated text reveals with staggered effects. - Created TopProgressBar for a dynamic loading indicator. - Added PointCloud mock for testing animations. - Developed useReducedMotion hook to handle user preferences for reduced motion. - Centralized animation configurations in config.ts for easier management. - Defined animation variants in variants.ts for consistent animation effects. - Established AnimationProvider to supply global animation settings. - Created tests for AnimationProvider and useReducedMotion hook. - Added SVG assets for branding and UI elements. - Documented frontend review plan and hero circuit board animation plan.
535 lines
14 KiB
Markdown
535 lines
14 KiB
Markdown
# Design Document: Premium Animations
|
|
|
|
## Overview
|
|
|
|
Este documento detalha o design técnico para implementar animações premium no portfolio evandro.dev.br, inspiradas no estilo suave e elegante do site on.energy. O sistema de animações será construído sobre Framer Motion e Lenis, mantendo a identidade visual existente (cores, tipografia Monument Extended, paleta --slash #ccff00) enquanto adiciona transições fluidas, scroll-triggered animations, parallax sutil e animações de hover sofisticadas.
|
|
|
|
### Objetivos Principais
|
|
|
|
1. Elevar a experiência visual com animações premium e fluidas
|
|
2. Manter performance consistente (60fps) em todos os dispositivos
|
|
3. Garantir acessibilidade completa com suporte a prefers-reduced-motion
|
|
4. Criar um sistema de animações reutilizável e escalável
|
|
5. Integrar smooth scroll global com Lenis
|
|
6. Implementar interações sofisticadas (magnetic buttons, custom cursor)
|
|
|
|
### Princípios de Design
|
|
|
|
- **Performance First**: Usar apenas propriedades GPU-accelerated (transform, opacity)
|
|
- **Accessibility First**: Respeitar prefers-reduced-motion em todas as animações
|
|
- **Progressive Enhancement**: Funcionalidade básica sem JavaScript, animações como enhancement
|
|
- **Consistency**: Usar curvas de easing e durações consistentes em todo o sistema
|
|
- **Subtlety**: Animações devem ser elegantes e discretas, não distrativas
|
|
|
|
## Architecture
|
|
|
|
### System Overview
|
|
|
|
O sistema de animações será organizado em camadas modulares:
|
|
|
|
```
|
|
Animation System
|
|
├── Core Layer (Hooks & Utilities)
|
|
│ ├── useScrollTrigger - Detecta elementos no viewport
|
|
│ ├── useParallax - Aplica efeitos parallax
|
|
│ ├── useReducedMotion - Detecta preferências de acessibilidade
|
|
│ ├── useMagneticEffect - Implementa efeito magnético
|
|
│ └── animationConfig - Configurações centralizadas
|
|
│
|
|
├── Component Layer (Wrappers Reutilizáveis)
|
|
│ ├── ScrollReveal - Wrapper para scroll-triggered animations
|
|
│ ├── StaggerContainer - Container para stagger animations
|
|
│ ├── ParallaxElement - Wrapper para parallax effects
|
|
│ ├── PageTransition - Transições entre páginas
|
|
│ ├── ImageReveal - Reveal animado para imagens
|
|
│ ├── TextReveal - Reveal animado para textos
|
|
│ └── CustomCursor - Cursor customizado
|
|
│
|
|
├── Integration Layer (Providers & Context)
|
|
│ ├── LenisProvider - Smooth scroll global
|
|
│ ├── AnimationProvider - Context para configurações
|
|
│ └── CursorProvider - Context para cursor customizado
|
|
│
|
|
└── Enhancement Layer (Micro-interactions)
|
|
├── Card hover effects
|
|
├── Button hover effects
|
|
├── Form field interactions
|
|
└── Loading states
|
|
```
|
|
|
|
### Technology Stack
|
|
|
|
- **Framer Motion 12.23.12**: Biblioteca principal de animações
|
|
- **Lenis 1.3.18**: Smooth scroll library
|
|
- **React 19.1.0**: Framework base
|
|
- **Next.js 15.5.9**: App Router para routing
|
|
- **TypeScript**: Type safety
|
|
- **Tailwind CSS 4**: Styling utilities
|
|
|
|
### Integration Points
|
|
|
|
1. **App Layout**: LenisProvider e AnimationProvider no root layout
|
|
2. **Page Components**: PageTransition wrapper em cada página
|
|
3. **Section Components**: ScrollReveal wrapper em seções principais
|
|
4. **Card Components**: Hover effects e stagger animations
|
|
5. **Form Components**: Micro-interactions em campos
|
|
6. **Hero Component**: Remoção da fase logo, manutenção de typing animation
|
|
|
|
## Components and Interfaces
|
|
|
|
### Core Hooks
|
|
|
|
#### useScrollTrigger
|
|
|
|
Hook para detectar quando elementos entram no viewport e disparar animações.
|
|
|
|
```typescript
|
|
interface UseScrollTriggerOptions {
|
|
threshold?: number; // Porcentagem do elemento visível (default: 0.2)
|
|
once?: boolean; // Executar apenas uma vez (default: true)
|
|
margin?: string; // Margin do viewport (default: "0px")
|
|
}
|
|
|
|
interface UseScrollTriggerReturn {
|
|
ref: RefObject<HTMLElement>;
|
|
isInView: boolean;
|
|
entry: IntersectionObserverEntry | null;
|
|
}
|
|
|
|
function useScrollTrigger(options?: UseScrollTriggerOptions): UseScrollTriggerReturn
|
|
```
|
|
|
|
**Implementação**: Wrapper sobre `useInView` do Framer Motion com configurações otimizadas.
|
|
|
|
#### useParallax
|
|
|
|
Hook para aplicar efeitos parallax em elementos durante o scroll.
|
|
|
|
```typescript
|
|
interface UseParallaxOptions {
|
|
speed?: number; // Velocidade do parallax (default: 0.5 = 50% mais lento)
|
|
direction?: "vertical" | "horizontal"; // Direção do movimento (default: "vertical")
|
|
disabled?: boolean; // Desabilitar parallax (usado com reduced motion)
|
|
}
|
|
|
|
interface UseParallaxReturn {
|
|
ref: RefObject<HTMLElement>;
|
|
style: MotionStyle;
|
|
}
|
|
|
|
function useParallax(options?: UseParallaxOptions): UseParallaxReturn
|
|
```
|
|
|
|
**Implementação**: Usa `useScroll` e `useTransform` do Framer Motion para calcular posição baseada no scroll.
|
|
|
|
#### useReducedMotion
|
|
|
|
Hook para detectar preferência de movimento reduzido do usuário.
|
|
|
|
```typescript
|
|
function useReducedMotion(): boolean
|
|
```
|
|
|
|
**Implementação**: Wrapper sobre `useReducedMotion` do Framer Motion, retorna `true` se `prefers-reduced-motion: reduce` está ativo.
|
|
|
|
#### useMagneticEffect
|
|
|
|
Hook para implementar efeito magnético em botões.
|
|
|
|
```typescript
|
|
interface UseMagneticEffectOptions {
|
|
radius?: number; // Raio de influência em pixels (default: 80)
|
|
strength?: number; // Força do efeito, max movement em pixels (default: 12)
|
|
disabled?: boolean; // Desabilitar efeito (usado com reduced motion)
|
|
}
|
|
|
|
interface UseMagneticEffectReturn {
|
|
ref: RefObject<HTMLElement>;
|
|
style: MotionStyle;
|
|
}
|
|
|
|
function useMagneticEffect(options?: UseMagneticEffectOptions): UseMagneticEffectReturn
|
|
```
|
|
|
|
**Implementação**: Rastreia posição do mouse, calcula distância ao elemento, aplica transform proporcional com spring physics.
|
|
|
|
### Component Wrappers
|
|
|
|
#### ScrollReveal
|
|
|
|
Componente wrapper para aplicar scroll-triggered animations.
|
|
|
|
```typescript
|
|
interface ScrollRevealProps {
|
|
children: React.ReactNode;
|
|
direction?: "up" | "down" | "left" | "right"; // Direção do slide (default: "up")
|
|
distance?: number; // Distância do movimento em pixels (default: 24)
|
|
duration?: number; // Duração da animação em segundos (default: 0.6)
|
|
delay?: number; // Delay antes da animação (default: 0)
|
|
threshold?: number; // Threshold do viewport (default: 0.2)
|
|
once?: boolean; // Executar apenas uma vez (default: true)
|
|
className?: string;
|
|
}
|
|
|
|
function ScrollReveal(props: ScrollRevealProps): JSX.Element
|
|
```
|
|
|
|
**Comportamento**:
|
|
- Normal: fade in + slide na direção especificada com blur inicial
|
|
- Reduced Motion: apenas fade in simples, sem movimento ou blur
|
|
|
|
#### StaggerContainer
|
|
|
|
Container para aplicar stagger animations em elementos filhos.
|
|
|
|
```typescript
|
|
interface StaggerContainerProps {
|
|
children: React.ReactNode;
|
|
staggerDelay?: number; // Delay entre elementos em segundos (default: 0.08)
|
|
initialDelay?: number; // Delay inicial antes do primeiro elemento (default: 0)
|
|
className?: string;
|
|
}
|
|
|
|
function StaggerContainer(props: StaggerContainerProps): JSX.Element
|
|
```
|
|
|
|
**Implementação**: Usa `motion.div` com `variants` para aplicar stagger automático aos filhos.
|
|
|
|
#### ParallaxElement
|
|
|
|
Wrapper para aplicar parallax effect em elementos.
|
|
|
|
```typescript
|
|
interface ParallaxElementProps {
|
|
children: React.ReactNode;
|
|
speed?: number; // Velocidade do parallax (default: 0.5)
|
|
direction?: "vertical" | "horizontal";
|
|
className?: string;
|
|
}
|
|
|
|
function ParallaxElement(props: ParallaxElementProps): JSX.Element
|
|
```
|
|
|
|
**Comportamento**:
|
|
- Normal: movimento parallax suave
|
|
- Reduced Motion: sem movimento, elemento estático
|
|
|
|
#### PageTransition
|
|
|
|
Wrapper para transições entre páginas.
|
|
|
|
```typescript
|
|
interface PageTransitionProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
function PageTransition(props: PageTransitionProps): JSX.Element
|
|
```
|
|
|
|
**Comportamento**:
|
|
- Normal: fade in de 0.4s com ease-out
|
|
- Reduced Motion: fade in de 0.15s
|
|
|
|
#### ImageReveal
|
|
|
|
Componente para reveal animado de imagens.
|
|
|
|
```typescript
|
|
interface ImageRevealProps {
|
|
src: string;
|
|
alt: string;
|
|
width?: number;
|
|
height?: number;
|
|
className?: string;
|
|
priority?: boolean;
|
|
}
|
|
|
|
function ImageReveal(props: ImageRevealProps): JSX.Element
|
|
```
|
|
|
|
**Comportamento**:
|
|
- Normal: clip-path reveal + fade in + scale (0.98 → 1) + blur (8px → 0px), duração 0.9s
|
|
- Reduced Motion: apenas fade in simples
|
|
|
|
#### TextReveal
|
|
|
|
Componente para reveal animado de textos por palavra.
|
|
|
|
```typescript
|
|
interface TextRevealProps {
|
|
text: string;
|
|
as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span"; // Tag HTML (default: "p")
|
|
staggerDelay?: number; // Delay entre palavras (default: 0.04)
|
|
className?: string;
|
|
}
|
|
|
|
function TextReveal(props: TextRevealProps): JSX.Element
|
|
```
|
|
|
|
**Comportamento**:
|
|
- Normal: cada palavra com blur (6px → 0px) + scale (0.95 → 1) + clip-path slide horizontal
|
|
- Reduced Motion: texto completo com fade in simples
|
|
|
|
#### CustomCursor
|
|
|
|
Componente para cursor customizado.
|
|
|
|
```typescript
|
|
interface CustomCursorProps {
|
|
size?: number; // Tamanho base do cursor (default: 40)
|
|
expandedSize?: number; // Tamanho expandido (default: 60)
|
|
color?: string; // Cor do cursor (default: "var(--slash)")
|
|
}
|
|
|
|
function CustomCursor(props: CustomCursorProps): JSX.Element
|
|
```
|
|
|
|
**Comportamento**:
|
|
- Desktop: cursor customizado com smooth delay, expande sobre interativos
|
|
- Touch devices: cursor padrão do sistema
|
|
- Reduced Motion: cursor padrão do sistema
|
|
|
|
### Providers
|
|
|
|
#### LenisProvider
|
|
|
|
Provider para smooth scroll global.
|
|
|
|
```typescript
|
|
interface LenisProviderProps {
|
|
children: React.ReactNode;
|
|
options?: {
|
|
duration?: number; // Duração do smooth scroll (default: 1.2)
|
|
easing?: number[]; // Curva de easing (default: [0.25, 0.0, 0.35, 1.0])
|
|
smoothWheel?: boolean; // Smooth wheel scroll (default: true)
|
|
smoothTouch?: boolean; // Smooth touch scroll (default: false)
|
|
};
|
|
}
|
|
|
|
function LenisProvider(props: LenisProviderProps): JSX.Element
|
|
```
|
|
|
|
**Implementação**: Inicializa Lenis, sincroniza com RAF, fornece instância via context.
|
|
|
|
#### AnimationProvider
|
|
|
|
Provider para configurações globais de animação.
|
|
|
|
```typescript
|
|
interface AnimationConfig {
|
|
reducedMotion: boolean;
|
|
easings: {
|
|
easeOut: string;
|
|
easeIn: string;
|
|
spring: { stiffness: number; damping: number };
|
|
};
|
|
durations: {
|
|
fast: number;
|
|
normal: number;
|
|
slow: number;
|
|
};
|
|
}
|
|
|
|
interface AnimationProviderProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
function AnimationProvider(props: AnimationProviderProps): JSX.Element
|
|
function useAnimationConfig(): AnimationConfig
|
|
```
|
|
|
|
**Implementação**: Detecta reduced motion, fornece configurações centralizadas via context.
|
|
|
|
#### CursorProvider
|
|
|
|
Provider para cursor customizado.
|
|
|
|
```typescript
|
|
interface CursorProviderProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
function CursorProvider(props: CursorProviderProps): JSX.Element
|
|
function useCursor(): {
|
|
setExpanded: (expanded: boolean) => void;
|
|
setColor: (color: string) => void;
|
|
}
|
|
```
|
|
|
|
**Implementação**: Gerencia estado do cursor, fornece métodos para controle via context.
|
|
|
|
## Data Models
|
|
|
|
### Animation Configuration
|
|
|
|
Configurações centralizadas para todas as animações.
|
|
|
|
```typescript
|
|
export const animationConfig = {
|
|
// Easing curves
|
|
easings: {
|
|
easeOut: "cubic-bezier(0.16, 1, 0.3, 1)",
|
|
easeIn: "cubic-bezier(0.4, 0, 1, 1)",
|
|
spring: {
|
|
default: { stiffness: 200, damping: 20 },
|
|
hover: { stiffness: 260, damping: 20 },
|
|
magnetic: { stiffness: 150, damping: 15 },
|
|
gentle: { stiffness: 60, damping: 20 },
|
|
},
|
|
},
|
|
|
|
// Durations (in seconds)
|
|
durations: {
|
|
instant: 0.15,
|
|
fast: 0.3,
|
|
normal: 0.6,
|
|
slow: 0.9,
|
|
verySlow: 1.2,
|
|
},
|
|
|
|
// Scroll trigger
|
|
scrollTrigger: {
|
|
threshold: 0.2,
|
|
once: true,
|
|
margin: "0px",
|
|
},
|
|
|
|
// Stagger
|
|
stagger: {
|
|
default: 0.08,
|
|
fast: 0.04,
|
|
slow: 0.12,
|
|
reducedMotion: 0.03,
|
|
},
|
|
|
|
// Parallax
|
|
parallax: {
|
|
speed: 0.5, // 50% mais lento que scroll normal
|
|
maxDistance: 100, // pixels
|
|
},
|
|
|
|
// Magnetic effect
|
|
magnetic: {
|
|
radius: 80, // pixels
|
|
strength: 12, // max movement in pixels
|
|
},
|
|
|
|
// Cursor
|
|
cursor: {
|
|
size: 40,
|
|
expandedSize: 60,
|
|
followDelay: 0.1,
|
|
},
|
|
|
|
// Reduced motion overrides
|
|
reducedMotion: {
|
|
duration: 0.15,
|
|
stagger: 0.03,
|
|
disableParallax: true,
|
|
disableMagnetic: true,
|
|
disableCursor: true,
|
|
},
|
|
} as const;
|
|
```
|
|
|
|
### Variant Presets
|
|
|
|
Presets de variants do Framer Motion para animações comuns.
|
|
|
|
```typescript
|
|
export const variants = {
|
|
// Fade in
|
|
fadeIn: {
|
|
initial: { opacity: 0 },
|
|
animate: { opacity: 1 },
|
|
exit: { opacity: 0 },
|
|
},
|
|
|
|
// Fade in + slide up
|
|
fadeInUp: {
|
|
initial: { opacity: 0, y: 24, filter: "blur(8px)" },
|
|
animate: { opacity: 1, y: 0, filter: "blur(0px)" },
|
|
exit: { opacity: 0, y: -24, filter: "blur(8px)" },
|
|
},
|
|
|
|
// Fade in + slide down
|
|
fadeInDown: {
|
|
initial: { opacity: 0, y: -24, filter: "blur(8px)" },
|
|
animate: { opacity: 1, y: 0, filter: "blur(0px)" },
|
|
exit: { opacity: 0, y: 24, filter: "blur(8px)" },
|
|
},
|
|
|
|
// Fade in + slide left
|
|
fadeInLeft: {
|
|
initial: { opacity: 0, x: 24, filter: "blur(8px)" },
|
|
animate: { opacity: 1, x: 0, filter: "blur(0px)" },
|
|
exit: { opacity: 0, x: -24, filter: "blur(8px)" },
|
|
},
|
|
|
|
// Fade in + slide right
|
|
fadeInRight: {
|
|
initial: { opacity: 0, x: -24, filter: "blur(8px)" },
|
|
animate: { opacity: 1, x: 0, filter: "blur(0px)" },
|
|
exit: { opacity: 0, x: 24, filter: "blur(8px)" },
|
|
},
|
|
|
|
// Scale in
|
|
scaleIn: {
|
|
initial: { opacity: 0, scale: 0.95 },
|
|
animate: { opacity: 1, scale: 1 },
|
|
exit: { opacity: 0, scale: 0.95 },
|
|
},
|
|
|
|
// Image reveal
|
|
imageReveal: {
|
|
initial: {
|
|
opacity: 0,
|
|
scale: 0.98,
|
|
filter: "blur(8px)",
|
|
clipPath: "inset(0 100% 0 0)",
|
|
},
|
|
animate: {
|
|
opacity: 1,
|
|
scale: 1,
|
|
filter: "blur(0px)",
|
|
clipPath: "inset(0 0% 0 0)",
|
|
},
|
|
},
|
|
|
|
// Text reveal (per word)
|
|
textReveal: {
|
|
initial: {
|
|
opacity: 0,
|
|
scale: 0.95,
|
|
filter: "blur(6px)",
|
|
clipPath: "inset(0 100% 0 0)",
|
|
},
|
|
animate: {
|
|
opacity: 1,
|
|
scale: 1,
|
|
filter: "blur(0px)",
|
|
clipPath: "inset(0 0% 0 0)",
|
|
},
|
|
},
|
|
|
|
// Stagger container
|
|
staggerContainer: {
|
|
animate: {
|
|
transition: {
|
|
staggerChildren: 0.08,
|
|
},
|
|
},
|
|
},
|
|
|
|
// Reduced motion variants
|
|
reducedMotion: {
|
|
fadeIn: {
|
|
initial: { opacity: 0 },
|
|
animate: { opacity: 1 },
|
|
exit: { opacity: 0 },
|
|
},
|
|
},
|
|
} as const;
|
|
```
|
|
|