choice-resolution

پارامترهای متناقض موشن را به خروجی‌ای پایدار، قابل‌پیش‌بینی و سازگار تبدیل می‌کند.

این Skill لایه‌های Recipe، Emotion، Brand و Platform را در یک pipeline مشخص ادغام می‌کند و پارامترهایی مثل duration، easing، exaggeration و secondary effects را به خروجی نهایی تبدیل می‌کند. ترتیب اعمال، محدودسازی و قوانین ادغام برای همه ورودی‌ها ثابت است.

برای ساخت انیمیشن‌های هماهنگ در Web، Mobile و Video مفید است؛ چون محدودیت‌های برند، بازه‌های زمانی پلتفرم و تنظیمات reduced motion را بدون انباشت یا تناقض کنترل می‌کند.

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

  • ادغام وزن‌دار چند Emotion و انتخاب مقدار غالب برای ویژگی‌های گسسته
  • اعمال محدودیت‌های Brand و حذف افکت‌های غیرمجاز مانند particles یا squash-stretch
  • سازگار‌سازی duration برای Web، Video، Mobile و دستگاه‌های Low-end
  • Clamp کردن پارامترها به بازه‌های معتبر و پشتیبانی از reduced motion
  • خروجی deterministic برای تولید موشن یکسان با ورودی‌های یکسان

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

  • تیم‌هایی که یک سیستم موشن مشترک برای محصول، ویدیو و پلتفرم‌های مختلف می‌سازند
  • پروژه‌هایی با چند ورودی هم‌زمان مثل Recipe، Emotion و Brand که ممکن است با هم تعارض داشته باشند
  • طراحی Design System یا animation pipeline که به خروجی قابل‌پیش‌بینی و قابل‌اعتبارسنجی نیاز دارد

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

  • ساخت مستقیم انیمیشن یا تولید فایل ویدیویی بدون داشتن لایه‌های انتخاب و پارامترهای ورودی
  • پروژه‌های ساده‌ای که فقط به یک duration و easing ثابت نیاز دارند

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

Choice Resolution

Resolves conflicting animation parameters from multiple sources into a single, deterministic output. Transforms parallel choice systems into a sequential resolution pipeline.

The Problem

Without unified resolution:

  • Emotion multipliers stack without bounds (1.2 × 1.4 = 1.68 → exceeds M3's 600ms max)
  • Brand "elegant" and emotion "playful" produce contradictory squash-stretch values
  • Video timing rules exist separately from the main choice pipeline
  • Same inputs can produce different outputs depending on evaluation order

Resolution Pipeline

Recipe (base) → Emotion (modifier) → Brand (constraint) → Platform (adaptation) → Bounds → Accessibility (override)

Each layer has a specific role:

LayerRoleOperation
RecipeBaseStarting values (duration, easing, principles)
EmotionModifierMultiplicative adjustments
BrandConstraintUpper bounds, feature restrictions
PlatformAdaptationPlatform-specific multipliers
BoundsClampFinal M3-compliant range enforcement
AccessibilityOverrideReduced motion bypasses everything

Resolution Algorithm

function resolveChoice(
  recipe: Recipe,
  emotions: WeightedEmotion[],
  brand: Brand,
  platform: Platform,
  context: Context
): ResolvedSpec {

  // 1. Start with recipe base
  let duration = recipe.duration;
  let easing = recipe.easing;
  let exaggeration = recipe.exaggeration ?? 1.0;
  let squashStretch = recipe.squashStretch ?? "none";
  let secondaryEffects = recipe.secondaryEffects ?? [];

  // 2. Blend emotions if multiple provided
  const blendedEmotion = blendEmotions(emotions);

  // 3. Apply blended emotion as modifier
  if (blendedEmotion) {
    duration *= blendedEmotion.durationMultiplier;
    easing = blendedEmotion.easing ?? easing;
    exaggeration *= blendedEmotion.exaggerationBoost ?? 1.0;
    squashStretch = blendedEmotion.squashStretch ?? squashStretch;
    secondaryEffects = mergeEffects(secondaryEffects, blendedEmotion.effects);
  }

  // 4. Apply brand as constraint
  if (brand) {
    exaggeration = Math.min(exaggeration, EXAGGERATION_LEVELS[brand.maxExaggeration]);
    if (!brand.allowsSquashStretch) {
      squashStretch = "none";
    }
    if (!brand.allowsParticles) {
      secondaryEffects = secondaryEffects.filter(e => e.type !== "particles");
    }
  }

  // 5. Apply platform adaptation
  duration *= platform.durationMultiplier;

  // 6. Clamp to bounds
  duration = clamp(duration, platform.minDuration, platform.maxDuration);
  exaggeration = clamp(exaggeration, 0, 1.25);

  // 7. Accessibility override
  if (context.reducedMotion) {
    return recipe.reducedMotion ?? DEFAULT_REDUCED_MOTION;
  }

  return { duration, easing, exaggeration, squashStretch, secondaryEffects };
}

Emotion Blending

When multiple emotions are specified, blend using weighted average:

function blendEmotions(emotions: WeightedEmotion[]): BlendedEmotion {
  if (emotions.length === 0) return null;
  if (emotions.length === 1) return emotions[0].emotion;

  // Normalize weights
  const totalWeight = emotions.reduce((sum, e) => sum + e.weight, 0);
  const normalized = emotions.map(e => ({ ...e, weight: e.weight / totalWeight }));

  // Numeric values: weighted average
  const durationMultiplier = normalized.reduce(
    (sum, e) => sum + e.emotion.durationMultiplier * e.weight, 0
  );

  // Discrete values: use dominant emotion
  const dominant = normalized.reduce((a, b) => a.weight > b.weight ? a : b);
  const easing = dominant.emotion.easing;
  const spring = dominant.emotion.spring;

  // Effects: union if either enables
  const effects = mergeEffects(
    ...normalized.map(e => e.emotion.secondaryEffects)
  );

  return { durationMultiplier, easing, spring, effects };
}

Merge Rules by Parameter

ParameterMerge TypeDescription
durationmultiplyRecipe × emotion × platform, then clamp
easingoverrideEmotion > recipe (if specified)
springoverrideEmotion > recipe (if specified)
exaggerationmultiply-capRecipe × emotion, capped by brand
squash-stretchminimumMore conservative of emotion/brand
secondary-effectsunion-filterCombine all, remove brand-forbidden

Platform Multipliers

PlatformDurationMaxNotes
Web1.0×600msM3 baseline
Video0.6×400msFaster for engagement
Mobile1.1×500msTouch feedback needs
Low-end0.8×400msPerformance priority

Brand Constraints

BrandMax ExaggerationSquashParticlesBounce
elegantsubtle✗✗✗
professionalsubtle✗✗✗
friendlymoderate✓✓✓
playfulexpressive✓✓✓
boldexpressive✓✓✓

Worked Example

Input:

  • Recipe: success-notification (duration: 300ms, spring-bouncy)
  • Emotion: celebratory (1.2× duration, expressive exaggeration)
  • Brand: professional (max subtle, no particles)
  • Platform: video (0.6×)

Resolution:

1. Recipe base:      300ms, spring-bouncy, exaggeration=1.0
2. + Emotion:        300 × 1.2 = 360ms, expressive → 1.0
3. + Brand cap:      exaggeration capped to subtle (0.5), no particles
4. + Platform:       360 × 0.6 = 216ms
5. + Bounds:         216ms (within 50-400ms) ✓
6. Accessibility:    Not reduced motion, proceed

Final: 216ms, spring-bouncy, exaggeration=0.5, no particles

Video-Specific Rules

When platform === "video":

  1. Apply 0.6× duration multiplier
  2. Enforce 400ms maximum
  3. Prefer immediate content start (no intro delay)
  4. Use 3-frame minimum (100ms at 30fps)
  5. Never auto-trigger celebrations

Validation Checklist

After resolution, verify:

  • ☐ Duration within platform bounds (50-600ms web, 50-400ms video)
  • ☐ Exaggeration between 0 and 1.25
  • ☐ No brand-forbidden features present
  • ☐ Reduced motion respected if enabled
  • ☐ Same inputs produce same outputs (deterministic)

Related Skills

  • animation-recipes - Recipe definitions
  • emotion-mapping - Emotion parameter sets
  • platform-adaptation - Platform-specific adjustments
  • accessibility - Reduced motion handling

See references/resolution-examples.md for additional worked examples.

دانلود ZIP