كيف تنشئ رسومًا متحركة جذابة للتحميل باستخدام CSS فقط لتطبيقات الويب

دقائق القراءة: 9
مرحبًا بك في منصة قيد! إذا كنت تتصفح الإنترنت مؤخرًا، فمن المحتمل أن تكون قد صادفت رسومًا متحركة تحميل (Loading Animation) دقيقة وجميلة تملأ محتوى الصفحة قبل ظهوره بشكل سلس. حتى عمالقة الشبكات الاجتماعية مثل فيسبوك يستخدمون هذا النهج لتقديم تجربة أفضل للمستخدم أثناء تحميل الصفحات. ولكن كيف يمكننا تحقيق ذلك باستخدام بعض أكواد CSS البسيطة فقط؟

ماذا سنبني في هذا المقال؟

سوف نقوم بإنشاء رسوم متحركة للتحميل باستخدام فئة CSS يمكنك تطبيقها على أي عنصر تريده تقريبًا (ضمن المعقول). يمنحك هذا مرونة كبيرة في الاستخدام ويجعل الحل بسيطًا وأنيقًا باستخدام CSS فقط.

معاينة لرسوم متحركة تحميل أنيقة باستخدام CSS

على الرغم من أن مقتطف الكود صغير جدًا ويمكنك نسخه ولصقه مباشرةً، إلا أنني سأشرح لك بالتفصيل ما يحدث خطوة بخطوة، وسأقدم لك مثالًا عمليًا لاستخدامه ديناميكيًا عند تحميل البيانات.

هل أحتاج إلى معرفة سابقة بالرسوم المتحركة قبل هذا الدرس؟

لا! سنشرح كل ما تحتاجه بالتفصيل. في الواقع، الرسوم المتحركة في هذا الدرس بسيطة نسبيًا، لذا دعنا نبدأ!

الجزء الأول: إنشاء رسوم متحركة التحميل الخاصة بنا

سيركز هذا الجزء الأول على تجميع رسوم متحركة التحميل ورؤيتها تعمل على موقع HTML ثابت. الهدف هو شرح كيفية إنشاء المقتطف البرمجي خطوة بخطوة. سنستخدم HTML و CSS فقط لهذا الجزء.

الخطوة 1: إنشاء بعض المحتوى النموذجي

للبدء، سنحتاج إلى بعض المحتوى النموذجي. لا توجد قيود حقيقية هنا؛ يمكنك إنشاء هذا باستخدام HTML و CSS الأساسيين، أو يمكنك إضافته إلى تطبيق Create React App الخاص بك! لأغراض الشرح، سأستخدم HTML و CSS مع بعض الأمثلة للمحتوى التي ستسمح لنا برؤية التأثير.

للبدء، أنشئ ملف HTML جديدًا. داخل ملف HTML هذا، املأه ببعض المحتوى الذي سيتيح لنا اللعب بالرسوم المتحركة الخاصة بنا. سأستخدم موقع fillerama الذي يستخدم سطورًا من مسلسلي التلفزيوني المفضل Futurama!

صفحة ويب HTML و CSS ثابتة تحتوي على محتوى من موقع fillerama.io

إذا كنت ستتبع الخطوات معي، فإليك شكل مشروعي:

my-css-loading-animation-
 static
 - index.html
 - main.css

الخطوة 2: البدء بفئة تحميل أساسية

لأساس عملنا، دعنا ننشئ فئة CSS جديدة. داخل ملف CSS الخاص بنا، دعنا نضيف:

.loading {
  background: #eceff1;
}

باستخدام هذه الفئة، دعنا نضيفها إلى عدد قليل أو كل عناصرنا. لقد أضفتها إلى بضعة فقرات وعناوين وقوائم.

<p class="loading"> For example...

صفحة ويب HTML و CSS ثابتة بخلفية رمادية للمحتوى

هذا يمنحنا خلفية أساسية، ولكننا على الأرجح نرغب في إخفاء هذا النص. عند التحميل، لن يكون لدينا هذا النص بعد، لذلك في معظم الحالات، سنرغب في استخدام نص وهمي أو ارتفاع ثابت. في كلتا الحالتين، يمكننا تعيين اللون إلى شفاف (transparent):

.loading {
  color: transparent;
  background: #eceff1;
}

صفحة ويب HTML و CSS ثابتة بخلفية رمادية ولون شفاف للمحتوى

إذا لاحظت مع عناصر القائمة، سواء قمت بتطبيق الفئة على عنصر القائمة الرئيسي (<ol> أو <ul>) مقابل عنصر القائمة نفسه (<li>)، فإنه يبدو وكأنه كتلة واحدة كبيرة. إذا أضفنا هامشًا صغيرًا إلى أسفل جميع عناصر القائمة، يمكننا رؤية اختلاف في كيفية عرضها:

li {
  margin-bottom: .5em;
}

اختلاف الأنماط بين تطبيق الفئة على القائمة الرئيسية أو عناصر القائمة

والآن بدأ الأمر يتضح، لكنه يبدو نوعًا ما مجرد عناصر نائبة. لذا دعنا نحرك هذا ليبدو وكأنه يتم تحميله بالفعل.

الخطوة 3: تصميم وتحريك فئة التحميل الخاصة بنا

قبل تحريك فئتنا فعليًا، نحتاج إلى شيء لتحريكه، لذا دعنا نضيف تدرجًا (gradient) إلى فئة .loading الخاصة بنا:

.loading {
  color: transparent;
  background: linear-gradient(100deg, #eceff1 30%, #f6f7f8 50%, #eceff1 70%);
}

هذا يعني أننا نريد تدرجًا خطيًا (linear-gradient) مائلًا بزاوية 100deg، حيث نبدأ باللون #eceff1 ويتلاشى إلى #f6f7f8 عند 30% ثم يعود إلى #eceff1 عند 70%.

خلفية بتدرج لوني خفيف قد تبدو كوهج

من الصعب رؤيته في البداية عندما يكون ثابتًا، قد يبدو وكأنه وهج على شاشتك! إذا كنت ترغب في رؤيته قبل المتابعة، فلا تتردد في اللعب بالألوان أعلاه لرؤية التدرج.

الآن بعد أن أصبح لدينا شيء لتحريكه، سنحتاج أولاً إلى إنشاء قاعدة @keyframes:

@keyframes loading {
  0% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0 50%;
  }
}

هذه القاعدة، عند تطبيقها، ستغير موضع الخلفية (background-position) من البدء عند 100% على المحور السيني إلى 0% على المحور السيني. باستخدام هذه القاعدة، يمكننا إضافة خاصية الرسوم المتحركة (animation) إلى فئة .loading الخاصة بنا:

.loading {
  color: transparent;
  background: linear-gradient(100deg, #eceff1 30%, #f6f7f8 50%, #eceff1 70%);
  animation: loading 1.2s ease-in-out infinite;
}

يحدد سطر animation الخاص بنا الإطار الرئيسي (keyframe) ليكون loading، ويخبره بالاستمرار لمدة 1.2s، ويضبط دالة التوقيت (timing function) على ease-in-out لجعله سلسًا، ويخبره بالتكرار إلى الأبد باستخدام infinite.

لا يوجد تغيير - الرسوم المتحركة لا تعمل

إذا لاحظت بعد الحفظ، فإنه لا يزال لا يفعل شيئًا. السبب في ذلك هو أننا نضبط التدرج من أحد طرفي عنصر DOM إلى الطرف الآخر، لذلك لا يوجد مكان للتحرك! لذا دعنا نحاول أيضًا تعيين background-size على فئة .loading الخاصة بنا.

.loading {
  color: transparent;
  background: linear-gradient(100deg, #eceff1 30%, #f6f7f8 50%, #eceff1 70%);
  background-size: 400%;
  animation: loading 1.2s ease-in-out infinite;
}

الآن، بما أن خلفيتنا تتجاوز عنصر DOM الخاص بنا (لا يمكنك رؤية هذا الجزء)، أصبح لديها بعض المساحة للتحرك والحركة، وبالتالي نحصل على الرسوم المتحركة الخاصة بنا!

رسوم متحركة التحميل الخاصة بنا جاهزة!

الجزء الثاني: استخدام رسوم متحركة التحميل في تطبيق ديناميكي

الآن بعد أن أصبح لدينا رسوم متحركة للتحميل، دعنا نضعها موضع التنفيذ بمثال أساسي حيث نقوم بمحاكاة حالة التحميل. تكمن الخدعة في استخدام هذا عادةً في أننا لا نملك المحتوى الفعلي متاحًا، لذلك في معظم الحالات، يتعين علينا محاكاته. لإظهار كيفية القيام بذلك، سنقوم ببناء تطبيق React بسيط باستخدام Next.js.

الخطوة 1: إنشاء تطبيق React كمثال باستخدام Next.js

انتقل إلى الدليل الذي تريد إنشاء مشروعك الجديد فيه وقم بتشغيل:

yarn create next-app
# أو
npm init next-app

سيطلب منك بعض الخيارات، لا سيما اسمًا سيحدد الدليل الذي سيتم إنشاء المشروع فيه ونوع المشروع. أنا أستخدم my-css-loading-animation-dynamic و “Default Starter App”.

إنشاء مشروع جديد باستخدام Next.js

بمجرد التثبيت، انتقل إلى دليلك الجديد وقم بتشغيل خادم التطوير الخاص بك:

cd [directory]
yarn dev
# أو
npm run dev

بدء تشغيل خادم التطوير باستخدام Next.js

بعد ذلك، دعنا نستبدل المحتوى في ملف pages/index.js الخاص بنا. سأستمد المحتوى من المثال السابق، لكننا سننشئه بطريقة مشابهة لما قد نتوقعه من واجهة برمجة تطبيقات (API). أولاً، دعنا نضيف المحتوى الخاص بنا ككائن فوق عبارة return الخاصة بنا:

const content = {
  header: `So, how 'bout them Knicks?`,
  intro: `What are their names? I'm Santa Claus! This opera's as lousy as it is brilliant! Your lyrics lack subtlety. You can't just have your characters announce how they feel. That makes me feel angry! Good news, everyone! I've taught the toaster to feel love!`,
  list: [
    `Yes! In your face, Gandhi!`,
    `So I really am important? How I feel when I'm drunk is correct?`,
    `Who are those horrible orange men?`
  ]
}

لعرض هذا المحتوى، داخل وسم <main>، دعنا نستبدل المحتوى بما يلي:

<main>
  <h1>{ content.header }</h1>
  <p>{ content.intro }</p>
  <ul>
    { content.list.map((item, i) => {
      return (
        <li key={i}>{ item }</li>
      )
    })}
  </ul>
</main>

بالنسبة للأنماط، يمكنك نسخ ولصق كل شيء من ملف main.css الخاص بالجزء الأول إلى وسمي <style> في أسفل صفحة الفهرس الخاصة بنا. سيتركنا ذلك مع:

محتوى أساسي مع Next.js

بهذا، يجب أن نعود إلى نقطة مشابهة للنقطة التي انتهينا عندها في الجزء الأول، باستثناء أننا لا نستخدم أيًا من رسوم متحركة التحميل بعد.

الخطوة 2: محاكاة تحميل البيانات من واجهة برمجة التطبيقات (API)

المثال الذي نعمل عليه بسيط جدًا. قد ترى هذا يأتي بشكل مسبق ثابت، لكن هذا يساعدنا في إنشاء عرض توضيحي واقعي يمكننا اختبار رسوم متحركة التحميل الخاصة بنا به. لمحاكاة حالة التحميل لدينا، سنستخدم useState و useEffect من React، و setTimeout التقليدي لتحميل بعض المحتوى “التحميل” مسبقًا، وبعد انتهاء setTimeout، نقوم بتحديث هذا المحتوى ببياناتنا الفعلية. في غضون ذلك، سنعرف أننا في حالة تحميل باستخدام مثيل منفصل من useState.

أولاً، نحتاج إلى استيراد تبعياتنا. في أعلى ملف pages/index.js الخاص بنا، أضف:

import { useState, useEffect } from 'react';

فوق كائن content الخاص بنا، دعنا نضيف بعض الحالات (state):

const [loadingState, updateLoadingState] = useState(true);
const [contentState, updateContentState] = useState({});

وفي محتوانا، يمكننا تحديث المثيلات لاستخدام هذه الحالة:

<h1>{ contentState.header }</h1>
<p>{ contentState.intro }</p>
<ul>
  { contentState.list.map((item, i) => {
    return (
      <li key={i}>{ item }</li>
    )
  })}
</ul>

بمجرد حفظ ذلك وتحميله، ستلاحظ أولاً أننا نحصل على خطأ لأن خاصية list غير موجودة في contentState الخاصة بنا، لذلك يمكننا إصلاح ذلك أولاً:

{ Array.isArray(contentState.list) && contentState.list.map(
  (item, i) => {
    return (
      <li key={i}>{ item }</li>
    )
  })}

وبعد أن يصبح ذلك جاهزًا، دعنا نضيف setTimeout الخاص بنا داخل useEffect لمحاكاة تحميل بياناتنا. أضف هذا تحت كائن content الخاص بنا:

useEffect(() => {
  setTimeout(() => {
    updateContentState(content);
    updateLoadingState(false)
  }, 2000);
}, [])

بمجرد حفظ الصفحة وإعادة تحميلها في متصفحك، ستلاحظ أنه لمدة ثانيتين لا يوجد لديك أي محتوى ثم يتم تحميله، مما يحاكي بشكل أساسي تحميل تلك البيانات بشكل غير متزامن.

الخطوة 3: إضافة رسوم متحركة التحميل الخاصة بنا

الآن يمكننا أخيرًا إضافة رسوم متحركة التحميل الخاصة بنا. للقيام بذلك، سنستخدم حالة التحميل (loadingState) التي قمنا بإعدادها باستخدام useState، وإذا كان المحتوى قيد التحميل، فسنضيف فئة .loading إلى عناصرنا.

قبل أن نفعل ذلك، بدلاً من إضافة هذه الفئة بشكل فردي إلى كل عنصر في DOM، قد يكون من المنطقي أكثر القيام بذلك باستخدام CSS وإضافة الفئة إلى العنصر الأب، لذا دعنا نفعل ذلك أولاً.

أولاً، قم بتحديث فئة .loading لاستهداف عناصرنا:

.loading h1,
.loading p,
.loading li {
  color: transparent;
  background: linear-gradient(100deg, #eceff1 30%, #f6f7f8 50%, #eceff1 70%);
  background-size: 400%;
  animation: loading 1.2s ease-in-out infinite;
}

ثم يمكننا إضافة فئتنا ديناميكيًا إلى وسم <main> الخاص بنا:

<main className={loadingState ? 'loading' : '' }>

ملاحظة: إذا كنت تستخدم Sass، يمكنك إدارة أنماط التحميل الخاصة بك عن طريق توسيع فئة .loading في المثيلات التي تريد استخدامها أو إنشاء عنصر نائب وتوسيعه!

وإذا قمت بتحديث الصفحة، ستلاحظ أنها لا تزال صفحة فارغة لمدة ثانيتين! المشكلة هي أنه عندما نقوم بتحميل المحتوى الخاص بنا، لا يوجد شيء داخل وسومنا يمكن أن يمنحها ارتفاعًا (line-height).

لا يوجد ارتفاع عندما لا يوجد محتوى

لكن يمكننا إصلاح ذلك! نظرًا لأن فئة .loading الخاصة بنا تجعل نصنا شفافًا، يمكننا ببساطة إضافة كلمة Loading لكل جزء من المحتوى:

const [contentState, updateContentState] = useState({
  header: 'Loading',
  intro: 'Loading',
  list: [
    'Loading',
    'Loading',
    'Loading'
  ]
})

ملاحظة: لا يمكننا استخدام مسافة فارغة هنا لأنها وحدها لن توفر لنا ارتفاعًا عند عرضها في DOM.

وبمجرد حفظ الصفحة وإعادة تحميلها، ستظهر أول ثانيتين بحالة تحميل تعكس محتوانا!

رسوم متحركة تحميل HTML و CSS

بعض الأفكار الإضافية

يمكن استخدام هذه التقنية على نطاق واسع. كونها فئة CSS يجعلها سهلة ومرنة للإضافة أينما تريد. إذا لم تكن من محبي تعيين نص Loading لحالة التحميل، فإن خيارًا آخر هو تعيين ارتفاع ثابت. المشكلة الوحيدة في ذلك هي أنه يتطلب المزيد من الصيانة لتعديل CSS لمطابقة شكل المحتوى الذي سيتم تحميله. بالإضافة إلى ذلك، لن يكون هذا مثاليًا دائمًا. في كثير من الأحيان، لن تعرف بالضبط كمية المحتوى الموجودة في الصفحة. الهدف هو محاكاة وإشارة إلى أنه سيكون هناك محتوى وأنه قيد التحميل حاليًا.

الخلاصة التقنية

يُعد إنشاء رسوم متحركة للتحميل باستخدام CSS فقط أسلوبًا فعالًا لتحسين تجربة المستخدم (UX) وجعل التطبيقات تبدو أكثر احترافية وسلاسة. من خلال استخدام خصائص CSS مثل linear-gradient و @keyframes مع تعديل background-size، يمكننا تحقيق تأثيرات بصرية جذابة دون الحاجة إلى مكتبات JavaScript إضافية. تتيح لنا هذه المرونة دمج الرسوم المتحركة بسهولة في أي مشروع، سواء كان ثابتًا أو ديناميكيًا مثل تطبيقات React و Next.js. كما أن محاكاة حالة التحميل باستخدام useState و useEffect في React توفر طريقة قوية لتقديم تجربة مستخدم متكاملة، حتى قبل ظهور المحتوى الفعلي. هذه التقنيات لا تساهم فقط في جمالية التطبيق، بل تعزز أيضًا من تفاعل المستخدم وصبره أثناء انتظار تحميل البيانات، مما يجعلها إضافة قيّمة لأي مشروع ويب حديث.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *