ویژگی تصویر

افکت موجی هنگام کلیک با CSS — راهنمای جامع

  /  CSS   /  افکت موجی هنگام کلیک با CSS
بنر تبلیغاتی الف

افکت موجی (ripple effect) یک انیمیشن بصری است که هنگام کلیک یا لمس روی دکمه‌ها و کارت‌ها از نقطه تعامل شروع شده و به اطراف گسترش می‌یابد. این افکت از طراحی متریال گوگل الهام گرفته شده و تجربه کاربری را جذاب‌تر و پاسخگوتر می‌کند. در این مقاله به پیاده‌سازی‌های مختلف — تنها با CSS و با کمک JavaScript کمینه —، نکات بهینه‌سازی، دسترس‌پذیری و موارد استفاده می‌پردازیم.

چرا از افکت موجی استفاده کنیم؟

  • بازخورد بصری بلافاصله پس از تعامل کاربر را فراهم می‌کند.
  • توجه را به عنصر درگیر جلب کرده و حس پاسخگویی افزایش می‌یابد.
  • به طراحی ظاهری و حرفه‌ای بودن رابط کمک می‌کند.

محدودیت‌ها و انتخاب تکنیک

افکت موجی که موقع کلیک دقیقا از نقطه تماس آغاز شود معمولاً به کمی JavaScript نیاز دارد تا مختصات کلیک محاسبه و در CSS متغیر شود. اگر طراحی شما نیاز به موجی از مرکز عنصر دارد، می‌توان فقط با CSS و pseudo-element آن را ساخت.

مثال 1 — افکت موجی ساده و CSS-only (موج از مرکز)

<button class="ripple-btn">Click me</button>

.ripple-btn{
  position: relative;
  overflow: hidden;
  padding: 12px 20px;
  border: none;
  background: #1976d2;
  color: #fff;
  border-radius: 6px;
  cursor: pointer;
}

.ripple-btn::after{
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 10px;
  height: 10px;
  background: rgba(255,255,255,0.4);
  border-radius: 50%;
  transform: translate(-50%,-50%) scale(1);
  opacity: 0;
  transition: transform 600ms ease, opacity 600ms ease;
  pointer-events: none;
}

.ripple-btn:active::after{
  transform: translate(-50%,-50%) scale(60);
  opacity: 1;
  transition: transform 600ms ease, opacity 600ms ease;
}

این کد یک دکمه ایجاد می‌کند که هنگام فشار (active) یک pseudo-element (بعد از) را بزرگ می‌کند تا افکت موجی از مرکز پدید آید. مزیت این روش سادگی و نداشتن جاوااسکریپت است، اما نقطه شروع موج همیشه مرکز دکمه خواهد بود و نه مکان دقیق کلیک.

مثال 2 — موجی از نقطه کلیک با CSS متغیر و JavaScript کمینه

<button class="ripple-pt" aria-label="Ripple button">Click me</button>

.ripple-pt{
  position: relative;
  overflow: hidden;
  padding: 12px 20px;
  border: none;
  background: #009688;
  color: white;
  border-radius: 6px;
  cursor: pointer;
}

.ripple-pt::after{
  content: "";
  position: absolute;
  left: var(--x, 50%);
  top: var(--y, 50%);
  width: var(--size, 10px);
  height: var(--size, 10px);
  background: rgba(255,255,255,0.35);
  border-radius: 50%;
  transform: translate(-50%,-50%) scale(1);
  opacity: 0;
  pointer-events: none;
  transition: transform 600ms ease, opacity 600ms ease;
  will-change: transform, opacity;
}

.ripple-pt.ripple-active::after{
  transform: translate(-50%,-50%) scale(60);
  opacity: 1;
}

/* JavaScript (minimal)
document.querySelectorAll('.ripple-pt').forEach(btn => {
  btn.addEventListener('click', function(e){
    const rect = this.getBoundingClientRect();
    const size = Math.max(rect.width, rect.height) * 0.2;
    const x = (e.clientX - rect.left) + 'px';
    const y = (e.clientY - rect.top) + 'px';
    this.style.setProperty('--x', x);
    this.style.setProperty('--y', y);
    this.style.setProperty('--size', size + 'px');
    this.classList.remove('ripple-active');
    // Force reflow to restart animation
    void this.offsetWidth;
    this.classList.add('ripple-active');
    // Remove class after animation
    setTimeout(()=> this.classList.remove('ripple-active'), 650);
  });
});

در این نمونه، با ثبت رویداد click مختصات کلیک نسبت به عنصر محاسبه شده و در CSS variables قرار می‌گیرد. سپس با افزودن کلاس فعال، pseudo-element از آن نقطه گسترش می‌یابد. این روش ترکیبی مزیت کنترل دقیق نقطه شروع موج را دارد و همچنان بار پردازشی کمی به همراه دارد.

نسخه بهینه‌شده و توصیه‌های عملکردی

/* Optimized CSS */.ripple-pt::after{
  will-change: transform, opacity;
  transform-origin: center center;
  backface-visibility: hidden;
  -webkit-transform: translate(-50%,-50%) scale(1);
  transition: transform 450ms cubic-bezier(.2,.8,.2,1), opacity 450ms linear;
}

در این بهینه‌سازی از will-change برای اطلاع به مرورگر درباره متغیرهایی که تغییر خواهند کرد استفاده شده تا GPU compositing فعال شود و بازپخش (repaint) کاهش پیدا کند. همچنین با ترکیب easing مناسب و مدت زمان معقول، انیمیشن روان‌تر و سریع‌تر اجرا می‌شود.

دسترس‌پذیری و رفتار برای کاربران خاص

  • از aria-label مناسب برای دکمه‌ها استفاده کنید تا خوانندگان صفحه‌خوان معنا را منتقل کنند.
  • برای کاربرانی که prefers-reduced-motion فعال کرده‌اند، انیمیشن را کاهش یا غیرفعال کنید:
@media (prefers-reduced-motion: reduce){
  .ripple-pt::after{
    transition: none;
    opacity: 0;
  }
}

این قطعه تضمین می‌کند که کاربرانی که حرکت کم‌تری را ترجیح می‌دهند، با مشکلی مواجه نشوند.

نکات فنی و بهترین روش‌ها

  • از transform: scale و opacity استفاده کنید نه تغییر ابعاد width/height برای کاهش layout thrashing.
  • از will-change فقط در عناصر کم و به‌طور موقتی استفاده کنید تا مصرف حافظه زیاد نشود.
  • برای buttonها اطمینان حاصل کنید که فوکوس و استیت‌های کیبورد (Enter/Space) نیز موج را ایجاد کنند تا دسترس‌پذیری حفظ شود.
  • برای عناصر بسیار بزرگ، اندازه موج را بر اساس قطر بزرگ‌ترین بعد تعیین کنید تا موج کل سطح را بپوشاند.

نمونه‌ی تکمیلی: فعال‌سازی از طریق کیبورد

btn.addEventListener('keydown', function(e){
  if(e.key === 'Enter' || e.key === ' '){
    // simulate click behavior: compute center or use last pointer pos
  }
});

برای دسترسی کامل، باید موج هنگام فشردن کلید Enter یا Space نیز فعال شود — مخصوصاً برای کاربرانی که از صفحه‌کلید استفاده می‌کنند.

مقایسه روش‌ها

روشمزیتمعایب
CSS-only (مرکزی)ساده، نیاز به JS نداردنقطه شروع ثابت؛ کمتر تعاملی
CSS + JS (مختصات کلیک)شروع از نقطه دقیق کلیک، طبیعی‌تریک Event listener اضافه و مقدار کمی پردازش

موارد استفاده واقعی و توصیه‌های طراحی

  • دکمه‌ها و آیتم‌های تعاملی در اپلیکیشن‌های موبایل و وب.
  • کارت‌ها، لیست‌ها و آیتم‌های قابل انتخاب برای برجسته‌سازی تعامل.
  • در پنل‌های با تعداد زیاد آیتم، از موج‌های سبک و کوتاه استفاده کنید تا منابع مصرفی پایین بماند.

جمع‌بندی و توصیه نهایی

افکت موجی می‌تواند تجربه کاربری را به‌طور محسوسی بهبود دهد اگر بهینه، دسترس‌پذیر و به‌جا استفاده شود. برای شروع، اگر به نقطه کلیک دقیق نیاز ندارید از نسخه CSS-only استفاده کنید. در صورتی که دقت و طبیعی بودن بیشتر مدنظر است، از ترکیب CSS متغیر همراه با JavaScript کمینه بهره ببرید. همواره به عملکرد، کاهش چشمک و دسترس‌پذیری توجه کنید.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: