emotional-journey

طراحی موشن هماهنگ با احساس کاربر در تمام مراحل تجربه

این Skill احساس کاربر را در مسیرهایی مثل کنجکاوی، تعامل، تمرکز، سردرگمی، موفقیت و ناکامی مدل می‌کند و برای هر حالت، الگوی حرکتی مناسب پیشنهاد می‌دهد. با استفاده از state machine، انتقال‌های معتبر و پروفایل‌های موشن، تجربه‌ای منسجم و قابل‌پیش‌بینی می‌سازید.

برای طراحی onboarding، checkout، بازیابی خطا و تجربه‌های تعاملی پیچیده کاربرد دارد. این Skill شدت، زمان‌بندی، easing و واکنش‌های بصری را با استانداردهایی مانند Russell's Circumplex، Material Design 3 و W3C DTCG هماهنگ می‌کند و reduced motion را نیز در نظر می‌گیرد.

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

  • نقشه‌ی state machine برای حالات احساسی و انتقال‌های معتبر
  • پروفایل موشن برای حالت‌هایی مانند curious، engaged، frustrated و accomplished
  • الگوهای آماده برای onboarding، checkout، بازیابی خطا و deep work
  • پیشنهاد duration، easing، intensity و بازخورد بصری برای هر انتقال
  • راهنمای پیاده‌سازی با GSAP، Framer Motion و TypeScript به‌همراه پشتیبانی از reduced motion

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

  • طراحان محصول و توسعه‌دهندگانی که برای flowهای چندمرحله‌ای سیستم موشن طراحی می‌کنند
  • تیم‌هایی که می‌خواهند انیمیشن‌ها احساس پیشرفت، موفقیت یا بازیابی از خطا را به‌درستی منتقل کنند
  • محصولات SaaS، onboarding، فرم‌های حساس و تجربه‌های تعاملی که به بازخورد رفتاری منسجم نیاز دارند

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

  • صفحه‌هایی که فقط به یک انیمیشن تزئینی مستقل و بدون جریان کاربر نیاز دارند
  • پروژه‌هایی که state machine یا مدل‌سازی حالت‌های احساسی برایشان بیش از حد پیچیده است
  • موشن‌های صرفاً سینمایی یا تبلیغاتی که با تعامل کاربر و وضعیت محصول ارتباطی ندارند

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

Emotional Journey

Map user emotional states and valid transitions to create journey-coherent motion. Ensures animations reflect and support the user's evolving emotional state.

Standards Reference

StandardDocumentation
W3C DTCGdesigntokens.org
Russell's CircumplexArousal-valence emotion model
Material Design 3m3.material.io/styles/motion

Quick Reference

Load from: tokens/emotional-journey.tokens.json

Journey States

StateArousalValenceMotion ProfileStable?
neutrallowneutralminimalYes
curiousmediumpositiveinvitingNo
engagedmedium-highpositiveresponsiveNo
focusedmediumneutralminimal (reduced)No
confusedmediumnegativeclarifyingNo
frustratedhighnegativerecoveryNo
accomplishedmedium-highpositiverewardingNo
delightedhighpositivecelebratoryTransient
anticipatingmediumneutralprogressiveNo

State Machine

                    ┌───────────────────────────────────────┐
                    │                                       │
         ┌─────────▼──────────┐        ┌──────────────────┐│
         │      NEUTRAL       │◄───────│    DELIGHTED     ││
         │   (stable rest)    │        │   (transient)    ││
         └────────┬───────────┘        └────────▲─────────┘│
                  │                             │          │
         ┌────────▼───────────┐        ┌────────┴─────────┐│
         │      CURIOUS       │◄──────►│   ACCOMPLISHED   │├─┐
         │   (exploring)      │        │   (completed)    ││ │
         └────────┬───────────┘        └────────▲─────────┘│ │
                  │                             │          │ │
         ┌────────▼───────────┐                 │          │ │
         │      ENGAGED       ├─────────────────┘          │ │
         │   (participating)  │                            │ │
         └────────┬───────────┘                            │ │
                  │                                        │ │
    ┌─────────────┼─────────────┐                          │ │
    │             │             │                          │ │
┌───▼────┐   ┌────▼────┐   ┌────▼─────┐                    │ │
│FOCUSED │   │CONFUSED │   │FRUSTRATED│────────────────────┘ │
│(deep)  │   │(lost)   │   │(blocked) │──────────────────────┘
└────────┘   └─────────┘   └──────────┘

State Transitions

Valid Transitions

FromToTrigger EventMotion Response
neutralcuriousnew-content-discoveredreveal
neutralengagedtask-startedguide
curiousengagedtask-startedguide
curiousconfusedcomplexity-encounteredclarify
engagedaccomplishedaction-successconfirm
engagedfrustratedaction-failedwarn
engagedfocuseddeep-work-enteredindicate
engagedanticipatingloading-startedindicate
focusedaccomplishedaction-successconfirm
frustratedengagedrecovery-offeredguide
frustratedclarifiedhelp-requestedclarify
accomplisheddelightedmilestone-reachedcelebrate
accomplishedengagedcontinue-taskguide
anticipatingaccomplishedasync-completeconfirm
anticipatingfrustratedasync-failedwarn

Journey Archetypes

Onboarding Journey

neutral → curious → engaged → accomplished → delighted

Motion strategy: Progressive revelation

// Onboarding flow with emotional journey
const onboardingSteps = [
  { state: 'neutral', motion: 'minimal' },
  { state: 'curious', motion: 'reveal', intent: 'reveal' },
  { state: 'engaged', motion: 'responsive', intent: 'guide' },
  { state: 'accomplished', motion: 'rewarding', intent: 'confirm' },
  { state: 'delighted', motion: 'celebratory', intent: 'celebrate' }
];

function getMotionForStep(stepIndex, event) {
  const currentState = onboardingSteps[stepIndex].state;
  const motionProfile = onboardingSteps[stepIndex].motion;

  return {
    duration: getStateDuration(currentState),
    easing: getStateEasing(currentState),
    intensity: getStateIntensity(currentState)
  };
}

Checkout Journey

engaged → focused → anticipating → accomplished

Motion strategy: Clear confirmation

// Checkout with minimal distraction during focused state
const checkoutMotion = {
  engaged: { intensity: 0.6, duration: 1.0 },
  focused: { intensity: 0.3, duration: 0.8 }, // Reduced motion
  anticipating: { intensity: 0.4, duration: 1.0 },
  accomplished: { intensity: 0.8, duration: 1.2 }
};

Error Recovery Journey

engaged → frustrated → clarified → engaged → accomplished

Motion strategy: Calm guidance

// Error recovery with calming transition
function handleError() {
  // 1. WARN: Show error
  gsap.to('.form', {
    x: 10,
    duration: 0.08,
    repeat: 3,
    yoyo: true
  });

  // 2. Brief pause (let frustration register)
  gsap.delayedCall(0.5, () => {
    // 3. GUIDE: Show recovery path
    gsap.to('.help-panel', {
      opacity: 1,
      y: 0,
      duration: 0.3,
      ease: 'power2.out'
    });
  });
}

Deep Work Journey

engaged → focused → accomplished

Motion strategy: Minimal distraction

// Deep work: 50% reduced motion intensity
const deepWorkModifier = 0.5;

const focusedMotion = {
  confirm: {
    duration: 200 * deepWorkModifier,
    scale: 1 + (0.05 * deepWorkModifier), // Subtle
    opacity: 1
  }
};

State Motion Profiles

Neutral

const neutralProfile = {
  motion: 'none',
  intensity: 0.1,
  secondary: false,
  ambient: 'allowed' // Subtle background motion OK
};

Curious

const curiousProfile = {
  motion: 'inviting',
  intensity: 0.5,
  easing: 'emphasized-decelerate',
  patterns: ['reveal', 'highlight', 'pulse'],
  encouragement: true
};

// Framer Motion
const curiousVariants = {
  hidden: { opacity: 0, y: 20, scale: 0.95 },
  curious: {
    opacity: 1,
    y: 0,
    scale: 1,
    transition: {
      duration: 0.4,
      ease: [0.05, 0.7, 0.1, 1]
    }
  }
};

Engaged

const engagedProfile = {
  motion: 'responsive',
  intensity: 0.6,
  easing: 'standard',
  feedback: 'immediate',
  patterns: ['confirm', 'guide']
};

// Quick, satisfying feedback
const engagedVariants = {
  tap: {
    scale: 0.98,
    transition: { duration: 0.05 }
  },
  release: {
    scale: 1,
    transition: { type: 'spring', stiffness: 400, damping: 20 }
  }
};

Frustrated

const frustratedProfile = {
  motion: 'recovery',
  intensity: 0.7, // Clear but not overwhelming
  maxDwellTime: 3000, // Must resolve within 3s
  requiresResolution: true,
  patterns: ['warn', 'guide'],
  calmingTransition: {
    delay: 500,
    easing: 'ease-out'
  }
};

// Show error, then guide to resolution
const frustratedSequence = gsap.timeline()
  .to('.error-indicator', {
    opacity: 1,
    scale: 1.1,
    duration: 0.15
  })
  .to('.error-indicator', {
    scale: 1,
    duration: 0.3,
    ease: 'power2.out'
  })
  .to('.help-suggestion', {
    opacity: 1,
    y: 0,
    duration: 0.3,
    delay: 0.3
  }, '-=0.1');

Delighted

const delightedProfile = {
  motion: 'celebratory',
  intensity: 1.0,
  transient: true, // Auto-exits after duration
  duration: 2000,
  patterns: ['celebrate', 'particle', 'glow'],
  secondary: true
};

// Full celebration with particles
const delightedAnimation = async () => {
  await gsap.to('.success-icon', {
    scale: 1.3,
    duration: 0.3,
    ease: 'back.out(2)'
  });

  triggerConfetti();

  await gsap.to('.success-icon', {
    scale: 1,
    duration: 0.4,
    ease: 'power2.out'
  });

  // Auto-transition to accomplished
  transitionTo('accomplished');
};

Transition Motion

Positive Transitions (toward positive valence)

const positiveTransition = {
  easing: 'emphasized-decelerate',
  durationMultiplier: 1.1,
  secondaryEffects: true
};

// frustrated → engaged (recovery)
gsap.to('.interface', {
  filter: 'saturate(1)',
  duration: 0.3,
  ease: 'power2.out'
});

Negative Transitions (toward negative valence)

const negativeTransition = {
  easing: 'emphasized-accelerate',
  durationMultiplier: 0.9,
  secondaryEffects: false
};

// engaged → frustrated
gsap.to('.interface', {
  filter: 'saturate(0.8)',
  duration: 0.2,
  ease: 'power2.in'
});

Implementation

State Manager

type EmotionalState =
  | 'neutral' | 'curious' | 'engaged' | 'focused'
  | 'confused' | 'frustrated' | 'accomplished'
  | 'delighted' | 'anticipating';

interface JourneyManager {
  currentState: EmotionalState;
  transition(event: TransitionEvent): void;
  getMotionProfile(): MotionProfile;
}

const validTransitions: Record<EmotionalState, EmotionalState[]> = {
  neutral: ['curious', 'engaged'],
  curious: ['engaged', 'confused', 'neutral'],
  engaged: ['accomplished', 'frustrated', 'focused', 'anticipating', 'curious'],
  focused: ['accomplished', 'frustrated', 'engaged'],
  confused: ['curious', 'engaged', 'frustrated', 'clarified'],
  frustrated: ['neutral', 'engaged', 'clarified'],
  accomplished: ['engaged', 'delighted', 'neutral', 'curious'],
  delighted: ['accomplished', 'engaged', 'neutral'],
  anticipating: ['engaged', 'frustrated', 'delighted', 'accomplished']
};

function canTransition(from: EmotionalState, to: EmotionalState): boolean {
  return validTransitions[from]?.includes(to) ?? false;
}

Reduced Motion

For prefers-reduced-motion: reduce:

StateFull MotionReduced Motion
curiousReveal animationInstant appear
engagedSpring feedbackOpacity pulse
frustratedShake + guideIcon + message
delightedCelebrationCheckmark only
anticipatingLoading animationStatic indicator

Cross-References

  • See emotion-mapping/ for emotion-to-parameter mapping
  • See intent/ for intent-emotion affinity
  • See narrative-arc/ for arc phase to emotional state mapping
  • See consequence/ for emotion-triggered chains

References

  • references/state-transitions.md — Complete transition table
  • references/journey-archetypes.md — Common journey patterns

دانلود ZIP