cinematic

افکت‌های سینمایی برای ساخت ویدیوها و رابط‌های کاربری تأثیرگذار

این Skill مجموعه‌ای از الگوها و کدهای آماده برای افزودن زبان بصری سینمایی به ویدیو و رابط کاربری است؛ از letterbox، fade و title card تا lower third، Ken Burns، slow motion، speed ramp و film grain.

با استفاده از tokenهای استاندارد و نمونه‌های CSS و JavaScript، می‌توانید ترنزیشن‌ها و سکانس‌های حرفه‌ای و هماهنگ بسازید و هم‌زمان پشتیبانی از prefers-reduced-motion را حفظ کنید.

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

  • الگوهای آماده برای letterbox، fade to black، cross dissolve و flash frame
  • ساخت title card، lower third و credits roll با زمان‌بندی دقیق
  • افکت‌های Ken Burns، vignette، film grain و حرکت‌های دوربین
  • مدیریت slow motion و speed ramp با `timeline` و easing
  • راهنمای tokenهای سینمایی و نسخه‌های مناسب برای reduced motion

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

  • ساخت intro، outro و تیزرهای ویدیویی با حال‌وهوای سینمایی
  • طراحی مستند، ویدیوهای روایی و ارائه‌های تصویری
  • افزودن ترنزیشن‌های نمایشی به landing page یا تجربه‌های تعاملی
  • تیم‌هایی که با CSS، JavaScript، Remotion یا After Effects کار می‌کنند

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

  • رابط‌های کاملاً کاربردی که به افکت‌های نمایشی یا ترنزیشن‌های طولانی نیاز ندارند
  • پروژه‌هایی که به تدوین کامل ویدیو، ضبط صدا یا تولید محتوای فیلم‌برداری‌شده نیاز دارند
  • محیط‌هایی که باید تمام حرکت‌ها حذف شوند و نسخه reduced motion کافی نیست

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

Cinematic

Film-style animation effects for video and cinematic content.

Standards Reference

StandardDocumentation
W3C DTCGdesigntokens.org
Remotionremotion.dev/docs
After Effectsadobe.com/products/aftereffects

Quick Reference

Load from: tokens/cinematic.tokens.json

Fade Durations

TokenValueUse
to-black.fast500msQuick fade out
to-black.normal1000msStandard fade
to-black.dramatic3000msDramatic ending
hold.normal1000msHold on black

Title Card

TokenValueUse
fade-in800msTitle appear
hold2000msTitle visible
fade-out600msTitle exit

Selection Guide

What cinematic effect?
│
├── LETTERBOX → Aspect ratio
│   ├── Subtle → 5% bars
│   ├── Standard → 2.35:1 ratio
│   └── Animated reveal
│
├── FADES → Scene transitions
│   ├── Fade to black → ending/transition
│   ├── Fade to white → flashback/dream
│   ├── Cross dissolve → scene blend
│   └── Dip to black → clear transition
│
├── TITLES → Text graphics
│   ├── Title card → centered text
│   ├── Lower third → name/info bar
│   └── Credits roll → scrolling text
│
├── SPEED → Timing effects
│   ├── Slow motion → dramatic emphasis
│   ├── Speed ramp → impact moment
│   └── Time freeze → pause
│
├── OVERLAYS → Visual effects
│   ├── Film grain → texture
│   ├── Vignette → focus
│   ├── Light leaks → warmth
│   └── Color grade → mood
│
└── CAMERA → Movement effects
    ├── Ken Burns → photo animation
    ├── Rack focus → depth
    └── Dolly zoom → vertigo

Patterns

Letterbox Animation

Cinematic aspect ratio bars.

CSS:

.letterbox {
  position: fixed;
  left: 0;
  right: 0;
  height: 0;
  background: black;
  z-index: 1000;
  transition: height 800ms cubic-bezier(0.4, 0, 0.2, 1);
  /* {motion.cinematic.letterbox.transition.normal} */
}

.letterbox-top {
  top: 0;
}

.letterbox-bottom {
  bottom: 0;
}

.cinematic-mode .letterbox {
  height: 12%; /* {motion.cinematic.letterbox.height.standard} */
}

JavaScript:

function setLetterbox(height, duration = 800) {
  const bars = document.querySelectorAll('.letterbox');

  bars.forEach(bar => {
    bar.animate([
      { height: bar.style.height || '0' },
      { height: `${height}%` }
    ], {
      duration,
      easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
      fill: 'forwards'
    });
  });
}

// Cinematic mode on
setLetterbox(12);

// Back to full screen
setLetterbox(0);

Fade to Black

Classic scene ending.

CSS:

.fade-overlay {
  position: fixed;
  inset: 0;
  background: black;
  opacity: 0;
  pointer-events: none;
  z-index: 9999;
}

.fade-to-black .fade-overlay {
  animation: fade-black 1000ms ease-in forwards;
  /* {motion.cinematic.fade.to-black.normal} */
}

@keyframes fade-black {
  to { opacity: 1; }
}

/* With hold */
.fade-through-black .fade-overlay {
  animation:
    fade-black 1000ms ease-in forwards,
    hold 1000ms 1000ms forwards,
    fade-in 1000ms 2000ms ease-out forwards;
}

@keyframes hold {
  from, to { opacity: 1; }
}

@keyframes fade-in {
  to { opacity: 0; }
}

JavaScript:

async function fadeToBlack(duration = 1000, hold = 1000) {
  const overlay = document.querySelector('.fade-overlay');

  // Fade out
  await overlay.animate([
    { opacity: 0 },
    { opacity: 1 }
  ], {
    duration,
    easing: 'ease-in',
    fill: 'forwards'
  }).finished;

  // Hold
  await new Promise(resolve => setTimeout(resolve, hold));

  // Fade back in
  await overlay.animate([
    { opacity: 1 },
    { opacity: 0 }
  ], {
    duration: duration * 0.8,
    easing: 'ease-out',
    fill: 'forwards'
  }).finished;
}

Cross Dissolve

Blend between two scenes.

CSS:

.dissolve-container {
  position: relative;
}

.dissolve-scene {
  position: absolute;
  inset: 0;
}

.dissolve-scene.outgoing {
  animation: dissolve-out 1000ms ease-in-out forwards;
  /* {motion.cinematic.cross-dissolve.normal} */
}

.dissolve-scene.incoming {
  animation: dissolve-in 1000ms ease-in-out forwards;
}

@keyframes dissolve-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

@keyframes dissolve-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

Title Card

Centered title with fade.

CSS:

.title-card {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: black;
  z-index: 1000;
}

.title-text {
  color: white;
  font-size: 4rem;
  font-weight: 300;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0;
  animation:
    title-in 800ms ease-out 500ms forwards,
    title-out 600ms ease-in 3300ms forwards;
  /* fade-in: {motion.cinematic.title-card.fade-in} */
  /* hold: {motion.cinematic.title-card.hold} */
  /* fade-out: {motion.cinematic.title-card.fade-out} */
}

@keyframes title-in {
  to { opacity: 1; }
}

@keyframes title-out {
  to { opacity: 0; }
}

Multi-line with stagger:

function animateTitleCard(lines, container) {
  lines.forEach((line, i) => {
    const element = document.createElement('div');
    element.textContent = line;
    element.style.opacity = 0;
    container.appendChild(element);

    // Staggered fade in
    element.animate([
      { opacity: 0, transform: 'translateY(10px)' },
      { opacity: 1, transform: 'translateY(0)' }
    ], {
      duration: 800, // {motion.cinematic.title-card.fade-in}
      delay: 500 + i * 200, // {motion.cinematic.title-card.stagger}
      easing: 'ease-out',
      fill: 'forwards'
    });
  });
}

Lower Third

Name/title graphic bar.

CSS:

.lower-third {
  position: fixed;
  bottom: 15%;
  left: 5%;
  transform: translateX(-100%);
  opacity: 0;
}

.lower-third.visible {
  animation: lower-third-in 400ms ease-out forwards;
  /* {motion.cinematic.lower-third.slide-in} */
}

.lower-third.exiting {
  animation: lower-third-out 300ms ease-in forwards;
  /* {motion.cinematic.lower-third.slide-out} */
}

@keyframes lower-third-in {
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes lower-third-out {
  to {
    transform: translateX(-50px);
    opacity: 0;
  }
}

.lower-third-bar {
  width: 4px;
  height: 100%;
  background: #3b82f6;
  margin-right: 16px;
}

.lower-third-name {
  font-size: 1.5rem;
  font-weight: 600;
}

.lower-third-title {
  font-size: 1rem;
  opacity: 0.8;
}

Ken Burns Effect

Pan and zoom on still images.

CSS:

.ken-burns {
  overflow: hidden;
}

.ken-burns img {
  animation: ken-burns 8000ms ease-in-out infinite alternate;
  /* {motion.cinematic.ken-burns.duration.normal} */
}

@keyframes ken-burns {
  from {
    transform: scale(1) translate(0, 0);
  }
  to {
    transform: scale(1.2) translate(-5%, -5%);
    /* {motion.cinematic.ken-burns.zoom-range.normal} */
  }
}

JavaScript (Randomized):

function kenBurns(image, duration = 8000) {
  const zoomRange = 0.2; // {motion.cinematic.ken-burns.zoom-range.normal}
  const panRange = 10;

  // Random start and end states
  const startScale = 1 + Math.random() * zoomRange;
  const endScale = 1 + Math.random() * zoomRange;

  const startX = (Math.random() - 0.5) * panRange;
  const startY = (Math.random() - 0.5) * panRange;
  const endX = (Math.random() - 0.5) * panRange;
  const endY = (Math.random() - 0.5) * panRange;

  image.animate([
    { transform: `scale(${startScale}) translate(${startX}%, ${startY}%)` },
    { transform: `scale(${endScale}) translate(${endX}%, ${endY}%)` }
  ], {
    duration,
    easing: 'ease-in-out',
    fill: 'forwards'
  });
}

Film Grain

Animated noise texture overlay.

CSS:

.film-grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9998;
  opacity: 0.08; /* {motion.cinematic.film-grain.intensity.moderate} */
}

.film-grain::before {
  content: '';
  position: absolute;
  inset: -200%;
  background-image: url('data:image/svg+xml,...'); /* Noise pattern */
  animation: grain 50ms steps(1) infinite;
  /* {motion.cinematic.film-grain.speed} */
}

@keyframes grain {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-5%, -5%); }
  20% { transform: translate(5%, 5%); }
  30% { transform: translate(-10%, 5%); }
  40% { transform: translate(5%, -5%); }
  50% { transform: translate(-5%, 10%); }
  60% { transform: translate(10%, -5%); }
  70% { transform: translate(-5%, -10%); }
  80% { transform: translate(5%, 5%); }
  90% { transform: translate(-10%, -5%); }
}

Vignette

Focus-drawing edge darkening.

CSS:

.vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9997;
  background: radial-gradient(
    ellipse at center,
    transparent 0%,
    transparent 60%, /* {motion.cinematic.vignette.size.normal} */
    rgba(0, 0, 0, 0.4) 100% /* {motion.cinematic.vignette.intensity.moderate} */
  );
  transition: opacity 500ms;
  /* {motion.cinematic.vignette.transition} */
}

Animated vignette:

function animateVignette(element, intensity, duration = 500) {
  element.animate([
    { opacity: 0 },
    { opacity: 1 }
  ], {
    duration,
    fill: 'forwards'
  });

  element.style.background = `radial-gradient(
    ellipse at center,
    transparent 0%,
    transparent 60%,
    rgba(0, 0, 0, ${intensity}) 100%
  )`;
}

Speed Ramp

Variable speed playback.

JavaScript:

class SpeedRamp {
  constructor(timeline, options = {}) {
    this.timeline = timeline;
    this.normalSpeed = options.normalSpeed || 1;
    this.slowSpeed = options.slowSpeed || 0.25;
    // {motion.cinematic.slow-motion.dramatic}
    this.rampDuration = options.rampDuration || 500;
    // {motion.cinematic.speed-ramp.ramp-duration.normal}
  }

  slowDown() {
    return this.rampTo(this.slowSpeed);
  }

  speedUp() {
    return this.rampTo(this.normalSpeed);
  }

  rampTo(targetSpeed) {
    const startSpeed = this.timeline.playbackRate;
    const startTime = performance.now();

    return new Promise(resolve => {
      const update = (time) => {
        const progress = Math.min((time - startTime) / this.rampDuration, 1);
        const eased = this.easeInOut(progress);

        this.timeline.playbackRate = startSpeed + (targetSpeed - startSpeed) * eased;

        if (progress < 1) {
          requestAnimationFrame(update);
        } else {
          resolve();
        }
      };

      requestAnimationFrame(update);
    });
  }

  easeInOut(t) {
    return t < 0.5
      ? 2 * t * t
      : 1 - Math.pow(-2 * t + 2, 2) / 2;
  }
}

Flash Frame

Impact/transition flash.

CSS:

.flash-frame {
  position: fixed;
  inset: 0;
  background: white;
  opacity: 0;
  pointer-events: none;
  z-index: 9999;
}

.flash-frame.flash {
  animation: flash 200ms ease-out;
  /* duration + recovery: {motion.cinematic.flash-frame.duration.single} + {motion.cinematic.flash-frame.recovery} */
}

@keyframes flash {
  0% { opacity: 0; }
  20% { opacity: 1; }
  100% { opacity: 0; }
}

Reduced Motion

For prefers-reduced-motion: reduce:

Full MotionReduced Alternative
Letterbox animationInstant letterbox
Fade transitionsFaster fade or cut
Ken BurnsStatic image
Film grainStatic or disabled
Speed rampsNormal speed only

Implementation:

@media (prefers-reduced-motion: reduce) {
  .letterbox {
    transition-duration: 0.01ms;
  }

  .ken-burns img {
    animation: none;
  }

  .film-grain::before {
    animation: none;
  }

  .fade-to-black .fade-overlay {
    animation-duration: 200ms;
  }
}

Cross-References

  • See camera/ for camera movements
  • See compositing/ for layer effects
  • See blur/ for depth of field
  • See opacity/ for fade patterns

References

  • references/film-techniques.md — Cinematography techniques

دانلود ZIP