exaggeration

شدت موشن را با توجه به برند، موقعیت و هدف تجربه کاربری تنظیم کنید.

این Skill اصل Exaggeration در انیمیشن را به تصمیم‌های عملی برای طراحی موشن تبدیل می‌کند؛ از micro-interactionهای ظریف تا لحظه‌های نمایشی و جشن‌گونه. با استفاده از سطح‌هایی مانند subtle، moderate و dramatic، مقدار scale، distance و timing به‌صورت هماهنگ تنظیم می‌شود.

برای ساخت انیمیشن‌های قابل‌خواندن و منسجم در CSS، GSAP، Framer Motion و Remotion کاربرد دارد و شدت حرکت را با شخصیت برند، مرحله روایت و نوع تعامل تطبیق می‌دهد. همچنین برای prefers-reduced-motion و جلوگیری از اغراق بیش‌ازحد راهکار ارائه می‌کند.

چه چیزی می‌گیرید

  • انتخاب سطح مناسب اغراق برای micro-interaction، اعلان، onboarding و لحظه‌های قهرمانانه
  • محاسبه و تنظیم هماهنگ scale، فاصله حرکت و مدت‌زمان انیمیشن
  • الگوهای آماده برای CSS Custom Properties، GSAP، Framer Motion و Remotion
  • تطبیق شدت موشن با شخصیت برند و مرحله روایت
  • راهنمای جلوگیری از موشن یکنواخت، نمایشی بیش‌ازحد و ناسازگار با دسترسی‌پذیری

برای چه کاری خوب است

  • طراحان و توسعه‌دهندگانی که برای UI، محصول دیجیتال یا ویدیو به سیستم شدت موشن نیاز دارند
  • پروژه‌هایی با برندهای حرفه‌ای، دوستانه، بازی‌محور یا جسور که باید شدت انیمیشن متفاوتی داشته باشند
  • ساخت state change، اعلان، onboarding، hero animation و celebration با بازخورد بصری واضح

برای چه کاری نیست

  • شبیه‌سازی‌های دقیق فیزیکی، رابط‌های علمی یا visualizationهایی که به حرکت کاملاً واقع‌گرایانه نیاز دارند
  • پروژه‌هایی که فقط به یک انیمیشن آماده و بدون تصمیم‌گیری درباره شدت، زمینه یا برند نیاز دارند
  • زمانی که انیمیشن باید بدون توجه به `prefers-reduced-motion` یا دسترسی‌پذیری اجرا شود

دربارهٔ این اسکیل

Exaggeration

Pushing motion beyond reality for emotional impact and visual clarity. Real-world motion is often too subtle to read on screen.

Core Principle

Exaggerate motion to communicate clearly. Perfect reality looks lifeless on screen; amplification creates life.

Real life:     Small gesture -> Barely visible on screen
Animation:     Exaggerated gesture -> Clear, readable, emotional

Token Reference

Load from: tokens/exaggeration.tokens.json

TokenMultiplierScale BoostUse Case
subtle1.1x1.05xEnterprise, professional
moderate1.3x1.15xConsumer apps
expressive1.6x1.30xPlayful, gaming
dramatic2.0x1.50xHero moments, celebrations
cartoon2.5x2.00xKids, character animation

Applying Exaggeration

Scale

// Base scale change: 10%
const baseScale = 1.1;

// With exaggeration multiplier
const exaggeratedScale = 1 + (baseScale - 1) * multiplier;

// subtle (1.1x):   1 + 0.1 * 1.1 = 1.11
// moderate (1.3x): 1 + 0.1 * 1.3 = 1.13
// dramatic (2.0x): 1 + 0.1 * 2.0 = 1.20

Distance

// Base movement: 20px
const baseDistance = 20;

// With exaggeration multiplier
const exaggeratedDistance = baseDistance * multiplier;

// subtle (1.1x):   22px
// moderate (1.3x): 26px
// dramatic (2.0x): 40px

Timing (optional stretch)

// Base duration: 300ms
const baseDuration = 300;

// With timing stretch (usually subtle)
const stretchedDuration = baseDuration * timingStretch;

// subtle (1.0x):   300ms (no change)
// moderate (1.1x): 330ms
// dramatic (1.3x): 390ms

Context Mapping

By Use Case

ContextLevelRationale
Micro-interactionssubtleFrequent, shouldn't distract
State changesmoderateClear feedback without drama
NotificationsexpressiveNeeds attention
CelebrationsdramaticPeak emotional moment
ErrorsmoderateNoticeable, not alarming
OnboardingexpressiveMemorable first impression

By Brand Attitude

Brand StyleLevelExamples
Elegant/LuxurysubtleHermes, Rolex, Apple
Professionalsubtle-moderateSalesforce, Notion
FriendlymoderateMailchimp, Slack
PlayfulexpressiveDiscord, Duolingo
BolddramaticNike, Spotify

By Narrative Phase

PhaseLevelPurpose
Introductionmoderate-expressiveGrab attention
Rising actionmoderateBuild steadily
ClimaxdramaticMaximum impact
Resolutionsubtle-moderateCalm return

Implementation Presets

CSS Custom Properties

:root {
  /* Exaggeration levels */
  --exaggeration-subtle: 1.1;
  --exaggeration-moderate: 1.3;
  --exaggeration-expressive: 1.6;
  --exaggeration-dramatic: 2.0;

  /* Current level (set via JS or class) */
  --exaggeration: var(--exaggeration-moderate);

  /* Derived values */
  --scale-boost: calc(1 + (0.1 * var(--exaggeration)));
  --distance-boost: calc(20px * var(--exaggeration));
}

/* Usage */
.card-hover {
  transform: scale(var(--scale-boost)) translateY(calc(var(--distance-boost) * -1));
}

/* Brand variants */
.brand-elegant { --exaggeration: var(--exaggeration-subtle); }
.brand-playful { --exaggeration: var(--exaggeration-expressive); }

GSAP

import { gsap } from 'gsap';
import tokens from '../tokens/exaggeration.tokens.json';

// Exaggeration utility
function withExaggeration(baseValue, level = 'moderate', type = 'distance') {
  const config = tokens.exaggeration[level].$value;

  switch (type) {
    case 'distance':
      return baseValue * config.distanceBoost;
    case 'scale':
      return 1 + (baseValue - 1) * config.scaleBoost;
    case 'duration':
      return baseValue * config.timingStretch;
    default:
      return baseValue * config.multiplier;
  }
}

// Example: Button press with configurable exaggeration
function buttonPress(button, level = 'moderate') {
  const baseScale = 0.95;
  const baseY = 2;

  return gsap.to(button, {
    scale: withExaggeration(baseScale, level, 'scale'),
    y: withExaggeration(baseY, level, 'distance'),
    duration: 0.1,
    ease: 'power2.out'
  });
}

// Success celebration with dramatic exaggeration
function celebrate(element) {
  const level = 'dramatic';

  return gsap.timeline()
    .to(element, {
      scale: withExaggeration(1.2, level, 'scale'),
      duration: 0.2,
      ease: 'back.out(2)'
    })
    .to(element, {
      y: withExaggeration(-30, level, 'distance'),
      duration: 0.3,
      ease: 'power2.out'
    })
    .to(element, {
      scale: 1,
      y: 0,
      duration: 0.4,
      ease: 'elastic.out(1, 0.5)'
    });
}

// Contextual exaggeration based on component
function animateComponent(component, action) {
  const contextMap = {
    'button': 'subtle',
    'card': 'moderate',
    'notification': 'expressive',
    'hero': 'dramatic'
  };

  const level = contextMap[component.type] || 'moderate';
  // Apply animation with appropriate level
}

Framer Motion

import { motion } from 'framer-motion';
import tokens from '../tokens/exaggeration.tokens.json';

// Hook for exaggeration values
const useExaggeration = (level: keyof typeof tokens.exaggeration) => {
  const config = tokens.exaggeration[level].$value;

  return {
    scale: (base: number) => 1 + (base - 1) * config.scaleBoost,
    distance: (base: number) => base * config.distanceBoost,
    duration: (base: number) => base * config.timingStretch,
    multiplier: config.multiplier
  };
};

// Component with configurable exaggeration
interface AnimatedCardProps {
  exaggeration?: 'subtle' | 'moderate' | 'expressive' | 'dramatic';
  children: React.ReactNode;
}

export const AnimatedCard: React.FC<AnimatedCardProps> = ({
  exaggeration = 'moderate',
  children
}) => {
  const ex = useExaggeration(exaggeration);

  return (
    <motion.div
      whileHover={{
        y: -ex.distance(10),
        scale: ex.scale(1.02),
        transition: { duration: ex.duration(0.2) }
      }}
      whileTap={{
        scale: ex.scale(0.98)
      }}
    >
      {children}
    </motion.div>
  );
};

// Celebration with maximum exaggeration
const celebrationVariants = {
  initial: { scale: 0, rotate: -180 },
  celebrate: (exLevel: string) => {
    const ex = tokens.exaggeration[exLevel].$value;
    return {
      scale: [0, ex.scaleBoost, 1],
      rotate: [-180, 10 * ex.multiplier, 0],
      transition: {
        duration: 0.6 * ex.timingStretch,
        times: [0, 0.6, 1],
        ease: 'backOut'
      }
    };
  }
};

export const Celebration: React.FC = () => (
  <motion.div
    variants={celebrationVariants}
    initial="initial"
    animate="celebrate"
    custom="dramatic"
  >
    Celebration!
  </motion.div>
);

Remotion

import { useCurrentFrame, interpolate, spring } from 'remotion';
import tokens from '../tokens/exaggeration.tokens.json';

// Exaggeration utilities for video
export const useExaggeration = (level: 'subtle' | 'moderate' | 'expressive' | 'dramatic') => {
  const config = tokens.exaggeration[level].$value;

  return {
    applyToScale: (base: number) => 1 + (base - 1) * config.scaleBoost,
    applyToDistance: (base: number) => base * config.distanceBoost,
    applyToDuration: (baseFrames: number) => Math.round(baseFrames * config.timingStretch)
  };
};

// Example: Title reveal with exaggeration
export const TitleReveal: React.FC<{
  text: string;
  startFrame: number;
  exaggeration?: 'subtle' | 'moderate' | 'expressive' | 'dramatic';
}> = ({ text, startFrame, exaggeration = 'moderate' }) => {
  const frame = useCurrentFrame();
  const ex = useExaggeration(exaggeration);

  const baseScale = 1.5;
  const baseY = 50;
  const baseDuration = 20;

  const progress = spring({
    frame: frame - startFrame,
    fps: 30,
    config: { damping: 12, stiffness: 100 }
  });

  const scale = interpolate(
    progress,
    [0, 0.5, 1],
    [ex.applyToScale(baseScale), ex.applyToScale(1.1), 1]
  );

  const y = interpolate(
    progress,
    [0, 1],
    [ex.applyToDistance(baseY), 0]
  );

  const opacity = interpolate(
    frame,
    [startFrame, startFrame + 10],
    [0, 1],
    { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
  );

  return (
    <div
      style={{
        transform: `scale(${scale}) translateY(${y}px)`,
        opacity
      }}
    >
      {text}
    </div>
  );
};

// Hero moment with dramatic exaggeration
export const HeroMoment: React.FC<{
  triggerFrame: number;
  children: React.ReactNode;
}> = ({ triggerFrame, children }) => {
  const frame = useCurrentFrame();
  const ex = useExaggeration('dramatic');

  // Dramatic entrance
  const progress = interpolate(
    frame,
    [triggerFrame, triggerFrame + ex.applyToDuration(30)],
    [0, 1],
    { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
  );

  const scale = interpolate(
    progress,
    [0, 0.6, 0.8, 1],
    [0, ex.applyToScale(1.3), ex.applyToScale(0.95), 1]
  );

  const y = interpolate(
    progress,
    [0, 0.6, 1],
    [ex.applyToDistance(100), ex.applyToDistance(-20), 0]
  );

  return (
    <div style={{ transform: `scale(${scale}) translateY(${y}px)` }}>
      {children}
    </div>
  );
};

Realistic vs Stylized Spectrum

StyleExaggerationPhysicsUse Case
Photorealistic1.0xAccurateSimulation, data viz
Natural1.1xEnhancedProfessional UI
Stylized1.5xAdjustedConsumer apps
Cartoon2.5x+ImpossibleEntertainment, kids

Accessibility

const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

// Reduce exaggeration for accessibility
const exaggerationLevel = prefersReducedMotion ? 'subtle' : determinedLevel;

// Or cap maximum exaggeration
const cappedLevel = prefersReducedMotion
  ? Math.min(level, tokens.exaggeration.subtle.$value.multiplier)
  : level;

Common Mistakes

  1. Uniform exaggeration - Different contexts need different levels
  2. Always dramatic - Save drama for meaningful moments
  3. Ignoring brand - Exaggeration must match brand personality
  4. Inconsistent - Same actions should have same exaggeration
  5. Overshadowing content - Animation serves content, not vice versa

Related Skills

  • anticipation - Exaggeration level affects anticipation wind-up intensity
  • squash-stretch - Exaggeration determines deformation magnitude
  • follow-through - Exaggeration affects overshoot and settle amplitude
  • secondary-action - Primary/secondary intensity gap follows exaggeration
  • arc-motion - Exaggeration affects arc height and trajectory drama

دانلود ZIP