تصميم الحركة: تفاعلات دقيقة ذات معنى
أهداف هذا الفصل
- ضبط مدة كل حركة ومنحناها وخاصيتها
- تنسيق الظهورات والانتقالات عند التمرير
- احترام الأداء وprefers-reduced-motion
صفحة مثالية، لكنها جامدة
صفحة هبوط Sereno الآن مطبَّعة، ومسوّمة بسمتين فاتحة وداكنة، ومتاحة. يمرّرها العميل بصمت ثم يقول: «جميلة جداً. لكنها... ساكنة. تشبه ملصقاً.» يترجم مدير Studio Mango: ينقصها الحركة (motion) — تلك الطبقة من الحركة التي تجعل الواجهة تبدو حية وتستجيب تحت الأصابع. وضع الفصل الخامس القاعدة (قصيرة، ناعمة، ذات معنى)؛ واليوم تبني نظام الحركة الكامل، بالصرامة نفسها التي اعتمدتها للألوان والطباعة.
فهذه هي النقطة المفصلية: الحركة تُصمَّم بـtokens وقواعد، لا بمؤثرات تُضاف حالةً حالة. الواجهة التي يتحرك فيها كل عنصر بسرعته الخاصة ومنحناه الخاص تبدو مرتجلة — تماماً كصفحة بمسافات عشوائية. عدد صغير من المدد، ومنحنى توقيعي أو اثنان، وقواعد استخدام: هذا ما ستسلّمه.
الحركة لغة وظيفية
قبل الضبط، السبب. الحركة المتقنة تؤدي أحد أدوار ثلاثة. التغذية الراجعة (feedback): تأكيد أن الفعل قد أُدرك — الزر الذي ينغرز قليلاً عند النقر يقول «سمعتك». التوجيه: إظهار من أين يأتي العنصر وإلى أين يذهب — اللوح الذي ينزلق من اليمين يشير إلى أنه سيُغلق نحو اليمين. الاستمرارية: ربط حالتين في الواجهة كي لا يكون التغيير قفزة عنيفة — البطاقة التي تتمدد بنعومة بدل نافذة منبثقة تظهر من العدم.
كل حركة لا تؤدي أياً من هذه الأدوار زخرفية — ضجيج. ولتطبيق تأمل، المعيار أكثر صرامة بعد: كل حركة يجب أن تُهدّئ، لا أن تشتت أبداً. اسأل نفسك عن كل حركة: «ماذا تقول للمستخدم؟». إن كان الجواب «لا شيء، لكنها جميلة»، فاحذفها. إنها نسخة الحركة من مطاردة العمومية.
الضبوط الثلاثة: المدة، المنحنى، الخاصية
القرار الأول: المدة، المتناسبة مع حجم التغيير. التغذيات الراجعة الدقيقة (المرور، التركيز، تحديد مربع) تعيش بين 100 و200 ميلي ثانية — الأقصر يبدو فورياً، والأطول يبدو رخواً. الانتقالات المتوسطة (ظهور بطاقة، تمدد أكورديون) بين 200 و300 ميلي ثانية. الحركات الكبيرة (لوح جانبي، تغيير صفحة) بين 300 و500 ميلي ثانية — وفوق ذلك ينتظر المستخدمُ الواجهةَ، والانتظار المتكرر يصبح انزعاجاً.
القرار الثاني: منحنى التسارع (easing). القاعدة الفيزيائية: ما يدخل الشاشة يتباطأ (ease-out — يصل العنصر سريعاً ويستقر بنعومة)، وما يخرج يتسارع (ease-in — يفرّ)، وما يتحرك في مكانه يفعل الاثنين (ease-in-out). أما linear فمحجوز للدورانات المستمرة كالـspinner. ولمنح Sereno توقيعاً، نعرّف منحنى cubic-bezier خاصاً، ناعماً وخفيف الكبح، يُستخدم في كل مكان — إنه مكافئ الحركة لخط العناوين.
:root {
/* tokens الحركة — النظام، لا مؤثرات معزولة */
--duration-fast: 150ms; /* تغذية راجعة دقيقة: hover, focus */
--duration-base: 250ms; /* انتقالات متوسطة: بطاقات، أكورديونات */
--duration-slow: 400ms; /* حركات كبيرة: ألواح، صفحات */
--ease-out-soft: cubic-bezier(0.25, 0.8, 0.35, 1); /* توقيع Sereno */
--ease-in-soft: cubic-bezier(0.55, 0, 0.7, 0.4);
}
.btn {
transition:
background-color var(--duration-fast) var(--ease-out-soft),
transform var(--duration-fast) var(--ease-out-soft),
box-shadow var(--duration-fast) var(--ease-out-soft);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0) scale(0.98); }--ease-out-soft) وامنع منحنيات التسارع المرتجلة في الموجّهات، تماماً كما تمنع الألوان الصلبة.الأداء: لا تحرّك إلا transform وopacity
القرار الثالث، الأكثر تقنية: الخاصية المتحركة. ليست كل خصائص CSS سواء. تحريك width أو height أو top أو margin يجبر المتصفح على إعادة حساب تخطيط المنطقة كلها في كل إطار — وعلى هاتف متوسط، تتقطع الحركة. أما تحريك transform (إزاحة، تحجيم، دوران) وopacity فيعالجه معالج الرسوميات مباشرة: سلاسة بـ60 إطاراً في الثانية حتى على عتاد متواضع.
الترجمة العملية: العنصر الذي «يرتفع» عند المرور يستخدم transform: translateY(-2px)، وأبداً ليس top: -2px. واللوح الذي يُفتح ينزلق بـtranslateX ولا يغيّر width. والظهور يجمع opacity وإزاحة صغيرة. أضف هذا القيد إلى كل موجّهات الحركة لديك — إنه من تلك المتطلبات التي يحترمها الذكاء الاصطناعي تماماً حين تُصاغ، وينساها مرة من اثنتين وإلا.
will-change: مفيد ظرفياً لتحضير حركة ثقيلة، لكنه يستهلك ذاكرة إذا وُضع في كل مكان «احتياطاً». القاعدة: لا تضفه إلا إذا تقطّعت حركة فعلاً، واسحبه بعد الحركة.التنسيق الحركي: ترتيب الظهورات
الصفحة لا تظهر كتلةً واحدة: إنها تُروى. التنسيق الحركي هو ترتيب الظهورات وفق هرمية القراءة: العنوان أولاً، فالعنوان الفرعي، فالـCTA أخيراً — كلٌّ مزاح 50 إلى 80 ميلي ثانية عن سابقه. هذا الإزاحة (الـ«stagger») قصيرة بما يكفي ليبدو المجموع سلساً، وطويلة بما يكفي لتتبع العين الخيط. بطاقات المزايا الثلاث تظهر تعاقبياً من اليسار إلى اليمين، لا كلها معاً.
flowchart LR L["تحميل القسم"] --> T["العنوان: ظهور فوري"] T --> ST["العنوان الفرعي: إزاحة 60 ميلي ثانية"] ST --> CTA["زر CTA: إزاحة 120 ميلي ثانية"] CTA --> C1["البطاقة 1"] C1 --> C2["البطاقة 2: زائد 70 ميلي ثانية"] C2 --> C3["البطاقة 3: زائد 140 ميلي ثانية"]
الظهورات عند التمرير، باعتدال
الكلاسيكية الكبرى لصفحات الهبوط: الأقسام التي تنكشف بنعومة مع التمرير. بجرعة مضبوطة، يوقّع الأثرُ إيقاع القراءة؛ وبجرعة زائدة، يحوّل الصفحة إلى مدينة ملاهٍ. قواعد الاعتدال: مسافة إزاحة قصيرة (12 إلى 16px، لا 100)، وظهور وحيد (يبقى العنصر مرئياً بعد انكشافه، ولا يعيد الحركة عند كل مرور)، ونوع مؤثر واحد للصفحة كلها — تلاشٍ + صعود خفيف، نقطة. لا تكبير هنا، ودوران هناك، وارتداد في مكان آخر.
/* CSS: الحالة الأولية والحالة المنكشفة */
.reveal {
opacity: 0;
transform: translateY(14px);
transition: opacity var(--duration-slow) var(--ease-out-soft),
transform var(--duration-slow) var(--ease-out-soft);
}
.reveal.is-visible { opacity: 1; transform: none; }// JS: IntersectionObserver — يكشف مرة واحدة فقط
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
observer.unobserve(entry.target); // مرة واحدة فقط
}
}
}, { threshold: 0.15 });
document.querySelectorAll('.reveal').forEach((el) => observer.observe(el));أضف نظام حركة كاملاً إلى صفحة هبوط Sereno (الكود مرفق): - tokens: --duration-fast 150ms، --duration-base 250ms، --duration-slow 400ms، منحنى توقيعي cubic-bezier(0.25, 0.8, 0.35, 1) - تغذيات راجعة دقيقة: hover وactive على الأزرار والبطاقات والروابط (transform + ظل، وأبداً لا top/width) - تنسيق الـhero: العنوان، ثم العنوان الفرعي (+60ms)، ثم CTA (+120ms)، بتلاشٍ + translateY(14px) - ظهورات عند التمرير: أقسام بتلاشٍ + صعود 14px، مرة واحدة فقط، عبر IntersectionObserver - القيود: لا تحرّك إلا transform وopacity؛ لا حركة تتجاوز 500ms؛ لا ارتداد ولا مؤثر مطاطي - أضف كتلة @media (prefers-reduced-motion: reduce) التي تعطّل الإزاحات والـstagger مع الإبقاء على تلاشيات بسيطة سريعة أعطِ CSS وJS منفصلين ومعلَّقين.
prefers-reduced-motion: غير قابل للتفاوض
جزء من المستخدمين يعاني اضطرابات دهليزية: حركات الواجهة — البارالاكس، والانزلاقات الواسعة، والتكبيرات — تسبب لهم دواراً وغثياناً حقيقيين. وآخرون يفضّلون ببساطة واجهة هادئة. كلهم يستطيعون تفعيل «تقليل الحركة» في نظامهم، وعلى CSS لديك الإصغاء عبر media query prefers-reduced-motion: reduce. التقليل لا يعني كسر كل شيء: نستبدل بالإزاحات تلاشياتٍ بسيطة قصيرة، ونحذف الحركات الزخرفية المحضة. تبقى الواجهة حية، وتكفّ عن التحرك في الفضاء.
@media (prefers-reduced-motion: reduce) {
.reveal {
transform: none; /* لا إزاحة مكانية بعد الآن */
transition: opacity 120ms ease; /* تلاشٍ بسيط قصير */
}
.btn:hover, .btn:active { transform: none; }
}ولتطبيق تأمل، ستكون المفارقة قاسية: أن تصيب بالغثيان مستخدماً جاء يطلب الهدوء. أدمج media query منذ أول موجّه حركة (كما أعلاه)، لا كترقيع في اللحظة الأخيرة — وأضف هذه النقطة إلى قائمة تحقق التسليم، في مرتبة التباينات نفسها.
متى لا نحرّك
تدقيق حركة لصفحة هبوط Sereno (الكود مرفق). أعدّ جدولاً شاملاً لكل حركة: - العنصر المعني، المُطلِق، المدة، المنحنى، الخصائص المتحركة - دورها: تغذية راجعة أو توجيه أو استمرارية — اكتب «لا شيء» إن كانت زخرفية محضة - المطابقة: الخصائص محصورة بـtransform/opacity؟ المدة دون 500ms؟ السلوك محدد تحت prefers-reduced-motion؟ ثم: 1. عدّد الحركات الواجب حذفها (الدور «لا شيء») مع جملة تبرير 2. عدّد المخالفات التقنية وتصحيحها 3. تحقق أن كل المدد والمنحنيات تأتي من tokens --duration-* و--ease-*، وأشر إلى أي قيمة صلبة
اختم بالجرد النقدي: اطلب من الذكاء الاصطناعي قائمة كل حركات الصفحة بمدتها وخاصيتها ودورها (تغذية راجعة، توجيه، استمرارية). كل سطر يبقى دوره فارغاً مرشّح للحذف. سترى دائماً تقريباً النتيجة نفسها: الصفحة النهائية تضم حركات أقل من النسخة الوسيطة، لكن كلاً منها في محلها. الحركة الفاخرة تشبه مونتاج فيلم جيد — لا نلاحظ القطعات، نشعر فقط بأن كل شيء يسيل. لن يقول العميل «حركات جميلة»؛ سيقول «إنها مهدّئة»، وهذه هي المهمة بالضبط.
السياق
«إنها ساكنة»، قال العميل. أمامك بعد ظهر لإضافة طبقة الحركة إلى صفحة هبوط Sereno: تغذيات راجعة دقيقة، وتنسيق الـhero، وانكشافات عند التمرير — كل ذلك سلساً على هاتف اقتصادي ومحترماً لـprefers-reduced-motion. وسيكون المدير متصلباً في نقطة واحدة: لا حركة مجانية واحدة.
التعليمات
- عرّف tokens الحركة: ثلاث مدد (150/250/400 ميلي ثانية) ومنحنى توقيعي cubic-bezier، مضافةً إلى نظام التصميم.
- أضف التغذيات الراجعة الدقيقة إلى الأزرار والبطاقات والروابط — transform وopacity فقط، انتقالات 150 ميلي ثانية.
- نسّق الـhero: العنوان فالعنوان الفرعي فالـCTA تعاقبياً بإزاحة 60 ميلي ثانية، بتلاشٍ + صعود 14px.
- ركّب الانكشافات عند التمرير بـIntersectionObserver: نوع مؤثر واحد، ومرة واحدة لكل عنصر.
- أضف كتلة prefers-reduced-motion التي تستبدل بكل إزاحة تلاشياً قصيراً، واختبرها بتفعيل الخيار في نظامك.
- اطلب الجرد النقدي: قائمة كل حركة بدورها (تغذية راجعة، توجيه، استمرارية) — واحذف كل سطر بلا دور.
باختصار
- الحركة تُصمَّم نظاماً: tokens مدد، منحنى توقيعي، قواعد استخدام — لا مؤثرات معزولة.
- كل حركة تؤدي دوراً: تغذية راجعة أو توجيه أو استمرارية — وإلا فهي ضجيج يُحذف.
- المدد: 100-200 ميلي ثانية للتغذيات الدقيقة، 200-300 للانتقالات، 300-500 للحركات الكبيرة.
- ease-out لما يدخل، وease-in لما يخرج، وease-in-out لما يتحرك؛ وlinear محجوز للدورانات المستمرة.
- لا نحرّك إلا transform وopacity: الخصائص الأخرى تقطّع الحركة على الهواتف المتواضعة.
- التنسيق يتبع هرمية القراءة بإزاحة 50-80 ميلي ثانية؛ وعند التمرير: مؤثر واحد، مرة واحدة، 12-16px.
- prefers-reduced-motion غير قابل للتفاوض: استبدال التلاشيات القصيرة بالإزاحات، منذ أول موجّه.
اختبار — تحقّق من فهمك
1. ما الأدوار الثلاثة المشروعة لحركة الواجهة؟
2. ما المدة المناسبة لتغذية راجعة دقيقة عند المرور؟
3. لماذا لا نحرّك إلا transform وopacity؟
4. أي منحنى تسارع لعنصر يدخل الشاشة؟
5. ماذا يجب أن تفعل كتلة prefers-reduced-motion: reduce؟
6. ما هو الـ«stagger» في تنسيق الظهور؟