Files
portifolio-evandrodevbr/.kiro/specs/premium-animations/design.md
T
Evandro Fonseca Junior 08dd046887 feat: add animated components for enhanced UI/UX
- 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.
2026-03-15 21:06:43 -03:00

14 KiB

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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;