ساخت افکت موجی روی دکمه با CSS
افکت موجی (ripple) یکی از جالبترین و پرکاربردترین انیمیشنها در طراحی رابط کاربری است. این افکت زمانی که کاربر روی یک دکمه کلیک یا لمس میکند، یک موج گردان از نقطه تعامل منتشر میشود که تجربه کاربری را طبیعیتر و پویاتر نشان میدهد. در این مقاله به چند روش عملی و بهینه برای ساخت افکت موجی با CSS میپردازیم — از نمونههای ساده CSS-only تا بهبودهای عملکرد و دسترسپذیری.
مفاهیم پایه
- Pseudo-elements (:before, :after) برای ایجاد لایه موجی بدون اضافه کردن عناصر اضافی در HTML کاربردی هستند.
- transform و opacity برای انیمیشن بهتر و بدون فشار زیاد روی پردازنده پیشنهاد میشود.
- برای انتشار موج از نقطه کلیک دقیقاً، معمولاً نیاز به JavaScript داریم؛ اما برای مثالهای مرکزی یا کلیکپذیر ساده میتوان فقط از CSS استفاده کرد.
مثال 1 — افکت موجی ساده با pseudo-element (مرکزی)
<button class="ripple-btn">Click me</button>
.ripple-btn {
position: relative;
overflow: hidden;
padding: 12px 24px;
border: none;
background: #1e88e5;
color: white;
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;
}
.ripple-btn:active::after {
transform: translate(-50%, -50%) scale(20);
opacity: 1;
transition: transform 300ms ease, opacity 300ms ease;
}این کد یک دکمه ساده میسازد که هنگام فشار (active) یک دایره سفید از مرکز بزرگ میشود و بهعنوان موج عمل میکند. مزیت این روش سادگی و عدم نیاز به JS است؛ اما موج همیشه از مرکز دکمه شروع میشود و از محل دقیق کلیک کاربر تبعیت نمیکند.
چرا از transform و opacity استفاده کردیم؟
انیمیشن روی خصیصههای layout-intensive (مثل width/height) باعث reflow و فشار بالاتر روی مرورگر میشود. استفاده از transform و opacity باعث میشود پردازش GPU سریعتر انجام شده و تجربه روانتری داشته باشیم.
مثال 2 — افکت موجی از نقطه کلیک (CSS + اندک JS)
<button class="ripple-btn-js">Click me</button>
.ripple-btn-js {
position: relative;
overflow: hidden;
padding: 12px 24px;
border: none;
background: #00b894;
color: white;
border-radius: 6px;
cursor: pointer;
}
.ripple {
position: absolute;
border-radius: 50%;
background: rgba(255,255,255,0.4);
transform: scale(0);
animation: ripple-effect 600ms linear;
pointer-events: none;
}
@keyframes ripple-effect {
to {
transform: scale(20);
opacity: 0;
}
}و بخش JavaScript (نمونه کوتاه):
document.querySelectorAll('.ripple-btn-js').forEach(btn => {
btn.addEventListener('click', function(e) {
const rect = btn.getBoundingClientRect();
const circle = document.createElement('span');
const size = Math.max(rect.width, rect.height);
circle.style.width = circle.style.height = size + 'px';
circle.style.left = (e.clientX - rect.left - size/2) + 'px';
circle.style.top = (e.clientY - rect.top - size/2) + 'px';
circle.classList.add('ripple');
btn.appendChild(circle);
setTimeout(() => circle.remove(), 700);
});
});این روش با ایجاد یک عنصر span پویا و موقعیتدهی بر اساس مختصات کلیک، موج را دقیقاً از نقطه لمس یا کلیک کاربر منتشر میکند. برای جلوگیری از تداخلهای ناخواسته، عنصر موج پس از پایان انیمیشن حذف میشود.
نکات بهینهسازی و دسترسپذیری
- استفاده از will-change برای پیشبینی انیمیشن: will-change: transform, opacity; با احتیاط و برای المانهای محدود استفاده شود تا مصرف حافظه افزایش نیابد.
- رعایت prefers-reduced-motion: کاربران دارای حساسیت حرکتی باید امکان غیرفعال کردن انیمیشن داشته باشند.
- برای واکنشگرا بودن، اندازه موج بر اساس max(width, height) محاسبه شود تا تمام دکمه را پوشش دهد.
- برای عملکرد بهتر از transform و opacity استفاده کنید و از تغییرات layout-intensive اجتناب کنید.
نمونه بهبود یافته با prefers-reduced-motion
.ripple { animation: ripple-effect 600ms linear; }
@media (prefers-reduced-motion: reduce) {
.ripple { animation: none; opacity: 0.6; transform: scale(1.5); }
}در این مثال، اگر کاربر تنظیمات سیستمعاملش را برای کاهش حرکت فعال کرده باشد، انیمیشن غیرفعال یا سادهتر میشود تا باعث ناراحتی نشود.
جدول مقایسه روشها
| روش | پیچیدگی | دقت محل کلیک | عملکرد |
|---|---|---|---|
| CSS-only (:after از مرکز) | کم | خیر (همیشه مرکز) | خوب |
| CSS + JS (ایجاد عنصر) | متوسط | بله | عالی با بهینهسازی |
| background radial-gradient (حرکت) | متوسط | محدود | متغیر |
نکتههای عملی برای پروژه
- در پروژههای بزرگ، component دکمه را طوری بسازید که گزینه موجی (ripple) بهصورت پراپ یا کلاس قابل غیرفعال کردن باشد.
- برای دکمههای تکراری از یک کلاس مشترک و حذف کردن عناصر موج پس از انیمیشن استفاده کنید تا DOM سنگین نشود.
- در لیستهای طولانی یا عناصر پویا، از event delegation بهجای ثبت listener برای هر دکمه استفاده کنید تا مصرف حافظه کاهش یابد.
جمعبندی
ایجاد افکت موجی روی دکمه با CSS بسیار قابل انجام است و بسته به نیازتان میتوانید از روش ساده CSS-only (مناسب برای افکتهای مرکزی و بدون JS) استفاده کنید یا با افزودن JS، موجی با دقت محل کلیک کاربر تولید کنید. همیشه به بهینهسازی عملکرد، استفاده از transform/opacity و رعایت دسترسپذیری (prefers-reduced-motion) توجه کنید تا تجربه کاربری مطلوب و روانی بهدست آورید.
آیا این مطلب برای شما مفید بود ؟




