يأتي Astro Rocket مزودًا بحركات وانتقالات في كل صفحة. ليست مجرد مؤثرات بصرية عشوائية، بل حركات مدروسة تجعل الموقع يبدو أسرع، أكثر احترافية، وأكثر حيوية. في هذه المقالة سنستعرض جميع الحركات داخل القالب: ماذا تفعل، أين توجد، وكيف يمكنك تخصيصها أو تعطيلها.
جميع الحركات في Astro Rocket تحترم إعداد prefers-reduced-motion. المستخدمون الذين قاموا بتفعيل تقليل الحركة من إعدادات نظام التشغيل لن يشاهدوا أي حركات مصغّرة. التنفيذ موجود داخل src/styles/global.css ولا يتطلب أي إعداد إضافي منك.
انتقالات الصفحات
أكثر حركة ملحوظة هي الانتقال بين الصفحات. يستخدم Astro Rocket مكوّن <ClientRouter /> المدمج من Astro، والذي يعتمد على واجهة View Transitions API الخاصة بالمتصفح لإنشاء انتقالات سلسة بين الصفحات.
بدلًا من إعادة تحميل الصفحة بشكل كامل، ينزلق المحتوى الحالي للأعلى ويختفي، بينما تظهر الصفحة الجديدة بحركة صعود ناعمة — تجربة تشبه التطبيقات الحديثة وتجعل التنقل يبدو فوريًا وسلسًا. يتم تفعيل ذلك عالميًا داخل src/layouts/BaseLayout.astro:
import { ClientRouter } from 'astro:transitions';
<!-- داخل <head> -->
<ClientRouter />
سطر واحد فقط يكفي. جميع الروابط الداخلية في الموقع تستفيد منه تلقائيًا، دون أي إعدادات إضافية لكل صفحة أو تحميل JavaScript غير ضروري.
الانتقال الافتراضي هو animate-slide-up ويتم تطبيقه على جميع الصفحات. يدعم Astro أيضًا انتقالات مثل fade و none لكل عنصر عبر transition:animate. ويمكنك استخدام transition:name للعناصر المشتركة بحيث تتحول في مكانها بدل أن تختفي ثم تعود — مفيد للهيدر، الشعارات، أو الصور المشتركة بين الصفحات.
حركة العدّاد أثناء التمرير
في الصفحة الرئيسية، قسم الإحصائيات — مثل سنوات الخبرة، المشاريع المنجزة، والعملاء حول العالم — لا يظهر كأرقام ثابتة. عند دخول القسم إلى الشاشة تبدأ الأرقام بالعد من الصفر حتى القيمة النهائية.
تُدار هذه الحركة بواسطة IntersectionObserver داخل src/pages/index.astro:
- يتم تفعيل المراقب مرة واحدة فقط لكل عنصر عند وصوله إلى 40% من مساحة الرؤية
- يستمر العد لمدة 1.2 ثانية باستخدام منحنى ease-out تكعيبي (
1 - (1 - progress)³) - بعد انتهاء الحركة يتم إيقاف المراقبة، لذلك لن تُعاد الحركة عند التمرير للأعلى أو للأسفل
لإضافة عدّاد لأي عنصر، أضف الخاصية data-countup مع الرقم المطلوب، ويمكنك إضافة data-suffix اختياريًا:
<p data-countup="50" data-suffix="+">50+</p>
يقوم السكربت تلقائيًا بالتعامل مع أي عنصر يحتوي على [data-countup] داخل الصفحة، لذلك يمكنك وضع العدادات في أي مكان.
حركة نتائج Lighthouse أثناء التمرير
مكوّن LighthouseScores الموجود في src/components/landing/LighthouseScores.astro يستخدم IntersectionObserver خاصًا به لتحريك أشرطة الأداء والأرقام عند ظهور القسم داخل الشاشة.
تبدأ الأشرطة من عرض صفر ثم تتمدد تدريجيًا حتى تصل للقيمة النهائية عند ظهور القسم. وكما في حركة العدّاد، يتم التنفيذ مرة واحدة فقط ثم تنظيف المراقب تلقائيًا. هذا يمنح قسم الأداء ظهورًا جذابًا يلفت الانتباه دون الحاجة إلى أرقام جامدة وثابتة.
الهيدر التفاعلي مع التمرير
الهيدر العائم يغيّر مظهره أثناء تمرير الصفحة. يتم التحكم بذلك عبر مستمع scroll داخل src/components/layout/Header.astro.
عندما تكون الصفحة في الأعلى (ضمن 60px من بداية التمرير)، يظهر الهيدر بدون خلفية — شفافًا مع نص معكوس ليتناسب مع قسم الـ Hero. وعندما يتجاوز المستخدم حاجز 60px يتم إضافة الخاصية data-scrolled:
if (window.scrollY > SCROLL_THRESHOLD) {
header.setAttribute('data-scrolled', '');
} else {
header.removeAttribute('data-scrolled');
}
تتكفل انتقالات CSS في عنصر الهيدر بتحريك الخلفية والحدود والألوان بسلاسة. لا يحدث أي تغيّر في التخطيط، فالهيدر يبقى ثابتًا ويتغير شكله فقط.
القيمة الافتراضية للحد هي 60px. ويمكنك تعديلها عبر تغيير ثابت SCROLL_THRESHOLD داخل Header.astro.
تأثيرات التحويم على البطاقات
كل بطاقة تفاعلية داخل الموقع ترتفع قليلًا عند تمرير المؤشر فوقها. يتم تطبيق ذلك مباشرة عبر أدوات Tailwind:
<div class="transition-all duration-200 hover:-translate-y-1 hover:shadow-md">
تتحرك البطاقة للأعلى بمقدار 4px مع ظل خفيف خلال 200ms. هذا يمنح إحساسًا تفاعليًا يساعد المستخدم على فهم أن البطاقة قابلة للنقر.
تستخدم الحركة duration-200 — سريعة بما يكفي لتبدو فورية، وبطيئة بما يكفي لتكون ملحوظة.
مكتبة الحركات المصغّرة
تقع مكتبة الحركات الكاملة داخل src/styles/global.css. هذه الأصناف مستخدمة في مختلف المكوّنات ويمكنك استعمالها داخل مكوّناتك الخاصة أيضًا.
حركات الظهور
.animate-fade-in /* ظهور تدريجي من الشفافية إلى الوضوح — 0.5s ease-out */
.animate-slide-up /* صعود من الأسفل مع تلاشي تدريجي — 0.5s ease-out */
.animate-slide-down /* نزول من الأعلى مع تلاشي تدريجي — 0.5s ease-out */
يمكن استخدامها لإظهار الأقسام، النوافذ المنبثقة، أو أي عنصر يظهر بعد تفاعل المستخدم.
حركات القوائم والطبقات العائمة
.animate-sheet-up /* ظهور اللوحة السفلية من خارج الشاشة — 0.25s spring */
.animate-sheet-down /* إخفاء اللوحة للأسفل — 0.2s */
.animate-menu-down /* فتح قائمة الهاتف للأسفل — 0.25s spring */
.animate-menu-up /* إغلاق القائمة للأعلى — 0.2s */
.animate-backdrop /* ظهور الخلفية الداكنة تدريجيًا — 0.2s ease-out */
.animate-backdrop-out /* اختفاء الخلفية الداكنة تدريجيًا — 0.2s ease-out */
تُستخدم هذه الحركات داخل مكوّنات Dialog والقائمة الجانبية للهاتف والطبقات العائمة.
يعطي منحنى الحركة (cubic-bezier(0.32, 0.72, 0, 1)) إحساسًا مرنًا وطبيعيًا عند الفتح.
حركات القوائم المنسدلة
.animate-dropdown-in /* نزول مع تكبير تدريجي — 0.2s spring */
.animate-dropdown-out /* انكماش للأعلى مع تصغير — 0.15s */
تُستخدم داخل مكوّن Dropdown. تنطلق الحركة من نقطة التفعيل نفسها مما يجعل الحركة متناسقة بصريًا.
حركات التفاعل والتنبيه
.animate-tab-enter /* انتقال ناعم بين محتوى التبويبات */
.animate-toast-in /* ظهور التنبيه الجانبي بحركة انزلاق — 350ms spring */
.animate-tooltip-in /* ظهور التلميحات مع تكبير وتلاشي */
.animate-shake /* اهتزاز قصير للتنبيه بالأخطاء — 400ms */
يستخدم التنبيه الجانبي --ease-spring لمنح دخول مرن ومريح بصريًا.
أما حركة الاهتزاز فهي مفيدة جدًا في التحقق من النماذج — يمكن تطبيقها على الحقول غير الصحيحة.
حالات التحميل
.animate-pulse /* نبض شفاف لعناصر Skeleton — 2s infinite */
.animate-spin /* دوران مستمر لأيقونات التحميل — 1s linear */
تُستخدم هذه الحركات داخل مكوّنات Skeleton و Progress وتستمر حتى إزالة العنصر من الصفحة.
تأخيرات التتابع
.delay-0 /* 0ms */
.delay-1 /* 50ms */
.delay-2 /* 100ms */
.delay-3 /* 150ms */
.delay-4 /* 200ms */
.delay-5 /* 250ms */
يمكن دمجها مع حركات الظهور لإنشاء تأثير ظهور متدرج:
<div class="animate-slide-up delay-0">العنصر الأول</div>
<div class="animate-slide-up delay-1">العنصر الثاني</div>
<div class="animate-slide-up delay-2">العنصر الثالث</div>
سيظهر كل عنصر بعد الآخر بفارق 50ms، مما يصنع حركة انسيابية توجه عين المستخدم تدريجيًا عبر المحتوى.
إضافة الحركات إلى محتواك الخاص
جميع هذه الأصناف متاحة في أي مكان داخل المشروع — سواء في الصفحات، المكوّنات المخصصة، أو عناصر Tailwind.
مثلًا لتحريك عنوان عند ظهوره:
<h2 class="animate-slide-up">عنواني</h2>
أما إذا كنت تريد تشغيل الحركة فقط عند دخول العنصر إلى الشاشة، يمكنك استخدام نفس نمط الصفحة الرئيسية عبر IntersectionObserver:
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
observer.unobserve(entry.target);
entry.target.classList.add('animate-slide-up');
});
}, { threshold: 0.2 });
document.querySelectorAll('[data-reveal]').forEach((el) => observer.observe(el));
أضف data-reveal لأي عنصر تريد إظهاره أثناء التمرير، وسيتولى المراقب الباقي.
تعطيل الحركات
لتعطيل جميع الحركات المصغّرة عالميًا (مع الإبقاء على انتقالات الصفحات)، قم بإزالة أو تعليق تعريفات الحركات داخل src/styles/global.css.
يوجد بالفعل بلوك @media (prefers-reduced-motion: reduce) في نهاية الملف لتعطيل الحركات المكثفة للمستخدمين الذين يفضلون تقليل الحركة.
أما لتعطيل انتقالات الصفحات بالكامل، قم بإزالة <ClientRouter /> من src/layouts/BaseLayout.astro.
ولتعطيل حركة عنصر معيّن فقط، احذف صنف الحركة من داخل المكوّن نفسه.