blur

ساخت افکت‌های Blur برای فوکوس، حرکت، عمق میدان و شیشه مات

این Skill مجموعه‌ای از الگوها و توکن‌های استاندارد برای ساخت انیمیشن‌های Blur ارائه می‌دهد؛ از Focus Pull و Motion Blur تا Depth of Field، Tilt-Shift، Radial Blur و Backdrop Blur.

با نمونه‌های آماده برای CSS، JavaScript، GSAP و Framer Motion می‌توانید افکت‌های لنز دوربین و انتقال فوکوس را سریع‌تر پیاده‌سازی کنید و هم‌زمان به Performance و prefers-reduced-motion توجه داشته باشید.

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

  • توکن‌های Blur در `tokens/blur.tokens.json` با شعاع‌ها و زمان‌بندی‌های آماده
  • الگوهای Focus Pull و Rack Focus برای جابه‌جایی فوکوس بین عناصر
  • نمونه‌های Motion Blur، Depth-Based Blur و Tilt-Shift با CSS، SVG و Canvas
  • پیاده‌سازی Backdrop Blur و افکت Frosted Glass با کنترل Performance
  • راهنمای Reduced Motion و بهینه‌سازی انیمیشن‌های `filter` و `backdrop-filter`

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

  • طراحان و توسعه‌دهندگان رابط کاربری که به ترنزیشن‌های سینمایی و فوکوس نیاز دارند
  • پروژه‌های وب مبتنی بر CSS، GSAP یا Framer Motion با عناصر متحرک
  • ساخت صحنه‌های چندلایه، افکت‌های عمق میدان، شیشه مات و Miniature Effect

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

  • پروژه‌هایی که به Blur سنگین روی نواحی بزرگ یا تعداد زیادی عنصر هم‌زمان نیاز دارند
  • زمانی که مرورگر هدف از `filter` یا `backdrop-filter` پشتیبانی نمی‌کند و جایگزین آماده ندارید

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

Blur

Blur effects for focus, motion, and depth simulation.

Standards Reference

StandardDocumentation
W3C DTCGdesigntokens.org
CSS Filterdeveloper.mozilla.org/CSS/filter
CSS Backdrop Filterdeveloper.mozilla.org/CSS/backdrop-filter

Quick Reference

Load from: tokens/blur.tokens.json

Blur Radius

TokenValueUse
none0pxSharp, in-focus
subtle2pxSlight softening
light4pxNoticeable softening
medium8pxClear defocus
strong16pxPronounced blur
extreme32pxAbstract softness

Focus Pull Duration

TokenValueUse
pull-duration-fast200msSnap focus
pull-duration-standard400msNatural pace
pull-duration-slow800msCinematic rack focus

Selection Guide

What type of blur?
│
├── FOCUS TRANSITION → Element coming into/out of focus
│   ├── Fast snap → 200ms
│   ├── Standard → 400ms
│   └── Cinematic → 800ms+
│
├── MOTION BLUR → Moving elements
│   ├── Subtle → distance-subtle (4px)
│   ├── Standard → distance-medium (12px)
│   └── Extreme → distance-extreme (48px)
│
├── DEPTH OF FIELD → Foreground/background separation
│   ├── Near blur → near-blur (8px)
│   └── Far blur → far-blur (4px)
│
├── TILT-SHIFT → Miniature effect
│   ├── Narrow band → focus-band-narrow (100px)
│   └── Wide band → focus-band-wide (400px)
│
└── BACKDROP → Background blur behind element
    ├── Subtle → light (4px)
    ├── Standard → medium (8px)
    └── Frosted glass → strong (16px)

Patterns

Focus Pull Transition

Animate blur to simulate camera focus change.

CSS:

.focus-pull {
  transition: filter 400ms ease-out; /* {motion.blur.focus.pull-duration-standard} */
}

.focus-pull.out-of-focus {
  filter: blur(8px); /* {motion.blur.radius.medium} */
}

.focus-pull.in-focus {
  filter: blur(0px); /* {motion.blur.radius.none} */
}

Framer Motion:

const focusVariants = {
  blurred: {
    filter: "blur(8px)", // {motion.blur.radius.medium}
    transition: { duration: 0.4 }
  },
  focused: {
    filter: "blur(0px)",
    transition: { duration: 0.4 }
  }
};

<motion.div
  variants={focusVariants}
  animate={isFocused ? "focused" : "blurred"}
/>

GSAP:

// Focus in
gsap.to(element, {
  filter: "blur(0px)",
  duration: 0.4,
  ease: "power2.out"
});

// Focus out
gsap.to(element, {
  filter: "blur(8px)",
  duration: 0.4,
  ease: "power2.out"
});

Rack Focus (Two Elements)

Shift focus between foreground and background.

JavaScript:

function rackFocus(foreground, background, duration = 0.8) {
  const tl = gsap.timeline();

  tl.to(foreground, {
    filter: "blur(8px)",
    duration: duration / 2,
    ease: "power2.inOut"
  }, 0)
  .to(background, {
    filter: "blur(0px)",
    duration: duration / 2,
    ease: "power2.inOut"
  }, duration / 4); // Slight overlap for smooth transition

  return tl;
}

Framer Motion:

function RackFocus({ focusTarget }) {
  return (
    <>
      <motion.div
        className="foreground"
        animate={{ filter: focusTarget === 'fg' ? 'blur(0px)' : 'blur(8px)' }}
        transition={{ duration: 0.4 }}
      />
      <motion.div
        className="background"
        animate={{ filter: focusTarget === 'bg' ? 'blur(0px)' : 'blur(4px)' }}
        transition={{ duration: 0.4 }}
      />
    </>
  );
}

Motion Blur Effect

Blur in direction of movement.

CSS (approximation with directional blur):

/* Horizontal motion blur using multiple shadows */
.motion-blur-h {
  filter: blur(0px);
  text-shadow:
    -4px 0 2px rgba(0,0,0,0.1),
    -2px 0 1px rgba(0,0,0,0.1),
    2px 0 1px rgba(0,0,0,0.1),
    4px 0 2px rgba(0,0,0,0.1);
}

/* Using SVG filter for true directional blur */
.motion-blur-svg {
  filter: url(#motion-blur);
}

SVG Filter:

<svg>
  <filter id="motion-blur">
    <feGaussianBlur in="SourceGraphic" stdDeviation="12,0" />
    <!-- 12px horizontal, 0px vertical -->
  </filter>
</svg>

Framer Motion (animate blur during motion):

function MovingElement() {
  const x = useMotionValue(0);
  const blur = useTransform(
    useVelocity(x),
    [-1000, 0, 1000],
    [12, 0, 12] // More velocity = more blur
  );

  return (
    <motion.div
      style={{
        x,
        filter: useMotionTemplate`blur(${blur}px)`
      }}
      drag="x"
    />
  );
}

Backdrop Blur (Frosted Glass)

Blur content behind an element.

CSS:

.frosted-glass {
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(16px); /* {motion.blur.radius.strong} */
  -webkit-backdrop-filter: blur(16px);
}

/* Animated backdrop blur */
.modal-backdrop {
  backdrop-filter: blur(0px);
  transition: backdrop-filter 300ms ease-out;
}

.modal-backdrop.visible {
  backdrop-filter: blur(8px); /* {motion.blur.radius.medium} */
}

Framer Motion:

<motion.div
  initial={{ backdropFilter: "blur(0px)" }}
  animate={{ backdropFilter: "blur(16px)" }}
  exit={{ backdropFilter: "blur(0px)" }}
  transition={{ duration: 0.3 }}
  style={{ background: "rgba(255,255,255,0.1)" }}
/>

Tilt-Shift Effect

Selective focus band for miniature effect.

CSS (gradient mask + blur):

.tilt-shift-container {
  position: relative;
}

.tilt-shift-blur {
  position: absolute;
  inset: 0;
  filter: blur(16px);
  mask-image: linear-gradient(
    to bottom,
    black 0%,
    transparent 30%,
    transparent 70%,
    black 100%
  );
  pointer-events: none;
}

Advanced Tilt-Shift:

function createTiltShift(container, options = {}) {
  const {
    focusPosition = 0.5, // 0-1, vertical position of focus band
    focusBandSize = 0.3, // 0-1, size of in-focus area
    blurAmount = 16      // px
  } = options;

  const top = (focusPosition - focusBandSize / 2) * 100;
  const bottom = (focusPosition + focusBandSize / 2) * 100;

  const blurLayer = document.createElement('div');
  blurLayer.style.cssText = `
    position: absolute;
    inset: 0;
    filter: blur(${blurAmount}px);
    mask-image: linear-gradient(
      to bottom,
      black 0%,
      black ${top}%,
      transparent ${top + 10}%,
      transparent ${bottom - 10}%,
      black ${bottom}%,
      black 100%
    );
    pointer-events: none;
  `;

  container.style.position = 'relative';
  container.appendChild(blurLayer);

  return {
    updateFocus(newPosition) {
      // Animate focus band position
      gsap.to(blurLayer, {
        maskImage: `linear-gradient(...)`, // Updated gradient
        duration: 0.5
      });
    }
  };
}

Depth-Based Blur

Blur based on z-depth in scene.

CSS Custom Properties:

.depth-element {
  --z-depth: 0; /* 0 = in focus, negative = near, positive = far */
  filter: blur(calc(abs(var(--z-depth)) * 2px));
  transition: filter 200ms ease-out;
}

/* Example depth layers */
.layer-near { --z-depth: -3; } /* 6px blur */
.layer-focus { --z-depth: 0; } /* 0px blur */
.layer-mid { --z-depth: 2; }   /* 4px blur */
.layer-far { --z-depth: 4; }   /* 8px blur */

Framer Motion:

function DepthScene({ focalDistance }) {
  const layers = [
    { z: 100, content: "Near" },
    { z: 300, content: "Focus" },
    { z: 500, content: "Far" }
  ];

  return layers.map(layer => {
    const distance = Math.abs(layer.z - focalDistance);
    const blur = Math.min(distance / 50, 16); // Max 16px blur

    return (
      <motion.div
        key={layer.z}
        animate={{ filter: `blur(${blur}px)` }}
        transition={{ duration: 0.4 }}
      >
        {layer.content}
      </motion.div>
    );
  });
}

Radial/Zoom Blur

Blur radiating from center point.

SVG Filter:

<svg>
  <filter id="radial-blur">
    <feGaussianBlur in="SourceGraphic" stdDeviation="0" result="blur1"/>
    <feGaussianBlur in="SourceGraphic" stdDeviation="2" result="blur2"/>
    <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur3"/>

    <feImage href="data:image/svg+xml,..." result="gradient"/>

    <feComposite in="blur1" in2="gradient" operator="in" result="masked1"/>
    <!-- Complex compositing for radial effect -->
  </filter>
</svg>

Canvas-based:

function radialBlur(ctx, centerX, centerY, maxBlur, samples = 10) {
  const canvas = ctx.canvas;
  const tempCanvas = document.createElement('canvas');
  tempCanvas.width = canvas.width;
  tempCanvas.height = canvas.height;
  const tempCtx = tempCanvas.getContext('2d');

  // Draw multiple scaled copies with offset
  for (let i = 0; i < samples; i++) {
    const scale = 1 + (i / samples) * 0.1;
    const alpha = 1 / samples;

    tempCtx.globalAlpha = alpha;
    tempCtx.save();
    tempCtx.translate(centerX, centerY);
    tempCtx.scale(scale, scale);
    tempCtx.translate(-centerX, -centerY);
    tempCtx.drawImage(canvas, 0, 0);
    tempCtx.restore();
  }

  ctx.drawImage(tempCanvas, 0, 0);
}

Reduced Motion

For prefers-reduced-motion: reduce:

Full MotionReduced Alternative
Focus pull animationInstant blur change
Motion blurNo blur, or static slight blur
Animated tilt-shiftStatic blur or none
Backdrop blur animationInstant backdrop blur

Implementation:

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

  .motion-blur-h {
    filter: none;
    text-shadow: none;
  }

  .tilt-shift-blur {
    /* Keep static blur, just don't animate */
    transition: none;
  }
}

Performance Considerations

  1. filter: blur() triggers GPU compositing — generally performant
  2. backdrop-filter can be expensive — limit to small areas
  3. Large blur radii (32px+) may cause performance issues
  4. Animated blur works best on simple elements
  5. Use will-change: filter before animating
.blur-animated {
  will-change: filter;
}

.blur-animated.animation-done {
  will-change: auto;
}

Cross-References

  • See reveals/ for blur + reveal combinations
  • See opacity/ for blur + fade combinations
  • See depth/ for 3D depth-based blur
  • See camera/ for cinematic focus effects
  • See motion-blur/ for legacy motion blur tokens

References

  • No additional references needed for this skill

دانلود ZIP