Kodax

الحركات (Animations) — CSS المتقدم

الحركات (Animations)

الانتقال يحتاج تغيير حالة (hover، صنف جديد) وله بداية ونهاية فقط. الحركة بـ @keyframes تعمل وحدها، بعدة مراحل، وتتكرر إن شئت: مؤشرات التحميل، ظهور العناصر، لفت الانتباه.

التعريف والاستخدام

@keyframes fade-up {
    from { opacity: 0; translate: 0 16px; }
    to   { opacity: 1; translate: 0 0; }
}
.reveal {
    animation: fade-up 400ms ease-out both;
}

مراحل متعددة

@keyframes pulse {
    0%, 100% { scale: 1; }
    50%      { scale: 1.08; }
}

خصائص animation

| الخاصية | المعنى | مثال |

|---|---|---|

| animation-name | اسم @keyframes | pulse |

| animation-duration | مدة الدورة الواحدة | 2s |

| animation-timing-function | المنحنى (لكل مرحلة) | ease-in-out |

| animation-delay | تأخير البدء (يقبل سالباً: يبدأ من المنتصف) | 200ms |

| animation-iteration-count | عدد التكرارات | 3 أو infinite |

| animation-direction | الاتجاه | alternate (ذهاب وإياب) |

| animation-fill-mode | الحالة قبل/بعد التشغيل | both |

| animation-play-state | إيقاف مؤقت | paused |

الاختصار: animation: pulse 2s ease-in-out infinite;

fill-mode: مشكلة "القفزة"

بعد انتهاء الحركة يعود العنصر لتنسيقه الأصلي. حركة ظهور تبدأ من opacity: 0 مع تأخير ستُظهر العنصر أولاً ثم تخفيه ثم تبدأ!

- forwards: يبقى على الإطار الأخير.

- backwards: يطبق الإطار الأول أثناء التأخير.

- both: الاثنان — الأكثر أماناً لحركات الظهور.

ظهور متتابع

.list-item { animation: fade-up 400ms ease-out both; }
.list-item:nth-child(2) { animation-delay: 80ms; }
.list-item:nth-child(3) { animation-delay: 160ms; }

أو بمتغير: style="--i: 2" في HTML و animation-delay: calc(var(--i) * 80ms).

مؤشر تحميل

@keyframes spin { to { rotate: 1turn; } }
.spinner {
    width: 32px;
    height: 32px;
    border: 3px solid rgb(0 0 0 / 15%);
    border-top-color: currentColor;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

from مُهمَل هنا: المتصفح يأخذ القيمة الحالية كبداية.

الأداء والوصولية

- حرّك transform/translate/scale/rotate و opacity فقط إن أمكن.

- الحركات اللانهائية تشتت الانتباه وتستهلك البطارية؛ استخدمها لمؤشرات التحميل فقط، وأوقفها عندما لا تلزم.

- prefers-reduced-motion إلزامي:

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

بعض الناس (اضطرابات الدهليز) تسبب لهم الحركة دواراً وغثياناً حقيقياً.

- تجنب الوميض أكثر من 3 مرات في الثانية (قد يسبب نوبات صرع).

الحركة المرتبطة بالتمرير (حديث)

animation-timeline: view() يربط تقدم الحركة بظهور العنصر أثناء التمرير — بلا JavaScript. الدعم ممتاز في Chrome و Edge ويتوسع؛ استخدمه داخل @supports (animation-timeline: view()) كتحسين إضافي.

تمرين

أنشئ مؤشر تحميل، وحركة ظهور متتابعة لبطاقات المشاريع بـ fill-mode: both، وأوقف كل الحركات مع prefers-reduced-motion. اختبر من أدوات المطور: Rendering ← Emulate prefers-reduced-motion.

/* حركة أساسية */
@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.fade-in {
    animation: fadeIn 1s ease forwards;
}

/* حركة متعددة المراحل */
@keyframes bounce {
    0%, 100% {
        transform: translateY(0);
    }
    50% {
        transform: translateY(-30px);
    }
}

.bouncing {
    animation: bounce 0.5s ease infinite;
}

/* حركة دوران */
@keyframes spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

.spinner {
    animation: spin 1s linear infinite;
}

/* حركة نبض */
@keyframes pulse {
    0% {
        transform: scale(1);
        box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.4);
    }
    70% {
        transform: scale(1.05);
        box-shadow: 0 0 0 20px rgba(52, 152, 219, 0);
    }
    100% {
        transform: scale(1);
    }
}

.pulse-button {
    animation: pulse 2s infinite;
}

/* حركة كتابة */
@keyframes typing {
    from {
        width: 0;
    }
    to {
        width: 100%;
    }
}

.typing-effect {
    overflow: hidden;
    white-space: nowrap;
    animation: typing 3s steps(30) forwards;
}

/* حركة تحميل */
@keyframes loading {
    0% {
        transform: translateX(-100%);
    }
    100% {
        transform: translateX(400%);
    }
}

.loading-bar {
    position: relative;
    overflow: hidden;
}

.loading-bar::after {
    content: '';
    position: absolute;
    width: 25%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
    animation: loading 1.5s infinite;
}
اختبر فهمك