EduFlow
إتقان CSS الحديثة
⁦0%⁩

الوحدة 4 · الحركة والمعمارية

أداء الرسم في المتصفّح

18 دقراءة 4 دبيئة تجريب الكود

الدرس في لمحة

اعرف أيّ تغييرات CSS تستدعي التخطيط أو الرسم أو التركيب وحده، وحرّك الخصائص الرخيصة، واستخدم content-visibility وaspect-ratio لتبقى الصفحات الطويلة سريعة ومستقرّة.

  1. 1خط الرسم في المتصفّح
  2. 2حرّك الخصائص الرخيصة
  3. 3تخطَّ العمل الذي لا تراه
  4. 4لا تحرّك الأشياء بعد ظهورها
  5. 5أين أنت الآن

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

بدا جدول Waypoint مكتملًا، ثم فتحه الفريق على هاتف Android عمره ثلاث سنوات عبر شبكة Wi-Fi المزدحمة في قاعة المؤتمر. تقطّع إشعار الحفظ، وبدا التمرير الأول لزجًا، ودفعت صور المتحدّثين النص إلى الأسفل وهي تُحمَّل. لم يكن شيء معطوبًا؛ كانت الصفحة ببساطة تؤدّي عملًا أكثر مما تحتاج.

أداء CSS يعود إلى سؤال واحد: كم من مراحل خط الرسم يجبر هذا التغيير المتصفّح على تشغيلها؟

خط الرسم في المتصفّح

بعد أن تغيّر الـ CSS شيئًا ما، يشغّل المتصفّح حتى أربع مراحل لتحديث الشاشة:

  1. الأنماط (Style): تحديد القواعد المنطبقة وحساب قيم كل عنصر.
  2. التخطيط (Layout): حساب حجم كل صندوق وموضعه.
  3. الرسم (Paint): ملء البكسلات: النصوص والألوان والحدود والظلال والصور.
  4. التركيب (Composite): تجميع الطبقات المرسومة على الـ GPU، مع تطبيق الـ transforms والشفافية.

السرّ أن كل تغيير لا يحتاج إلا إلى البدء من مرحلة معيّنة. غيّر width فيجب أن يعمل التخطيط، ثم الرسم، ثم التركيب. وغيّر background-color فيمكن تخطّي التخطيط. وغيّر transform أو opacity على عنصر له طبقة خاصة به، فيستطيع المتصفّح تخطّي التخطيط والرسم معًا، وتحريك بكسلات موجودة أصلًا على الـ GPU.

أربع مراحل لخط الرسم من اليسار إلى اليمين: الأنماط، التخطيط، الرسم، التركيب. صف لـ width وtop وmargin يمرّ بالأربع كلها. وصف لـ color وbackground وbox-shadow يتخطّى التخطيط. وصف لـ transform وopacity يتخطّى التخطيط والرسم. الأنماط التخطيط الرسم التركيب width, top المراحل الأربع، في كل إطار box-shadow مُتخطّاة translate مُتخطّاة
أيّ المراحل يستدعيها تغيير الخاصية

في نقرة واحدة لا يهمّ الفرق. أما في حركة تعمل 60 مرة في الثانية، أو تغيير نمط يلمس 500 عنصر، فهو ما يقرّر هل ستبدو الصفحة سلسة أم لا.

حرّك الخصائص الرخيصة

كان إشعار الحفظ يتحرّك هكذا:

CSS
@keyframes toast-in {
  from { left: -320px; }
  to   { left: 16px; }
}

left خاصية أبعاد: كل إطار يعيد تشغيل التخطيط للإشعار (ويفحص هل تحرّك أيّ شيء آخر)، ثم يعيد الرسم. والإصلاح يُبقي موضع الإشعار ثابتًا ويحرّك صورته بدلًا من ذلك:

CSS
.toast {
  position: fixed;
  left: 16px;
  bottom: 16px;
  animation: toast-in 250ms ease-out;
}

@keyframes toast-in {
  from { translate: -336px 0; opacity: 0; }
  to   { translate: 0 0;      opacity: 1; }
}

النتيجة البصرية نفسها، بجزء صغير من العمل. ولهذا استخدمت كل حركة في هذه الدورة translate وscale وopacity وأخواتها. والمنطق نفسه ينطبق على الـ transitions: مرورٌ يغيّر box-shadow يعيد الرسم في كل إطار، وهذا مقبول لبطاقة واحدة وملحوظ لشبكة من خمسين بطاقة تتحرّك معًا. وإظهار ظلّ مرسوم مسبقًا على pseudo-element بتحريك opacity هو البديل الأرخص حين يهمّ الأمر.

تخطَّ العمل الذي لا تراه

يرسم الجدول جلسات يومين، لكن المستخدم يرى ملء شاشة واحدة في كل مرة. content-visibility: auto تتيح للمتصفّح تخطّي التخطيط والرسم للأقسام الواقعة خارج الشاشة:

CSS
.day {
  content-visibility: auto;
  contain-intrinsic-size: auto 1200px;
}

المحتوى المتخطّى ليس له حجم، لذا تعطي contain-intrinsic-size المتصفّح تقديرًا يستخدمه كعنصر نائب؛ ومع auto يتذكّر الحجم الحقيقي بعد أن يُرسم القسم مرة. ودونها تنهار الأقسام إلى الصفر ويقفز شريط التمرير أثناء التمرير. يبقى المحتوى في الـ DOM وفي شجرة إمكانية الوصول، ويظل البحث داخل الصفحة يعمل. وcontent-visibility: auto ضمن Baseline منذ سبتمبر 2025، حين أضاف Safari 26 القيمة auto؛ والمتصفّح الذي لا يدعمها يرسم كل الأقسام ببساطة، كما كان يفعل من قبل.

لا تحرّك الأشياء بعد ظهورها

مقياس Cumulative Layout Shift، أحد مقاييس Core Web Vitals، يقيس قفز المحتوى أثناء تحميل الصفحة. وفي CSS يكون المذنب المعتاد وسائط بلا مساحة محجوزة. صورة متحدّث لها width: 100% بلا ارتفاع يكون ارتفاعها 0px حتى تصل الصورة، ثم تدفع كل شيء إلى الأسفل. احجز المساحة:

CSS
.speaker img {
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
}

سمتا width وheight على <img> تؤدّيان العمل نفسه، لأن المتصفّحات تستنتج منهما نسبة الأبعاد. وللخطوط من الويب، font-display: swap مع خط احتياطي بمقاييس مشابهة تمنع النص من إعادة الانسياب حين يُحمَّل الخط.

أين أنت الآن

على مدى عشرين درسًا نقلت جدول Waypoint من ملف أنماط يصارعك إلى ملف تستطيع توقّعه: الـ layers و:where() تحسم التعارضات، والـ custom properties تكشف واجهات الـ components، وgrid والـ subgrid والـ container queries تتولّى التخطيط، وoklch() وlight-dark() تتولّيان الألوان، والخصائص المنطقية تجعله يعمل بالعربية، والحركة لطيفة ورخيصة. والعادة التي تستحق أن تحتفظ بها هي عادة الدرس الأول: قبل أن تغيّر الـ CSS، توقّع ما سيفعله المتصفّح، ثم تحقّق.

أهم النقاط

  • كل تغيير بصري يمرّ ببعض مراحل الأنماط والتخطيط والرسم والتركيب؛ خصائص الأبعاد تستدعي التخطيط، والخصائص البصرية تستدعي الرسم، وtransform وopacity تستطيعان تخطّي الاثنين.
  • حرّك translate وscale وrotate وopacity؛ أما تحريك left أو width أو margin فيعيد تشغيل التخطيط في كل إطار.
  • content-visibility: auto مع contain-intrinsic-size تتيح للمتصفّح تخطّي رسم الأقسام الواقعة خارج الشاشة في الصفحة الطويلة.
  • حجز المساحة بـ aspect-ratio (أو بسمتي width وheight) يمنع إزاحات التخطيط حين تُحمَّل الصور والمحتوى المضمّن.
  • قِس قبل أن تحسّن، باستخدام لوحة Performance في DevTools وخيار وميض الرسم (paint flashing)، وأزل will-change حين تنتهي الحركة.

قراءات إضافية

تحقّق من فهمك

3 أسئلة

السؤال 1 من 3

إشعار منبثق ينزلق إلى الداخل بتحريك left من -320px إلى 16px. على هاتف متوسّط المواصفات يتقطّع. ما أفضل إصلاح؟

السؤال 2 من 3

أيّ تغيير في الخصائص يسبّب عادةً إعادة رسم دون تخطيط؟

السؤال 3 من 3

في الجدول 60 بطاقة جلسة على مدى يومين، والرسم الأول بطيء. أضفت content-visibility: auto إلى قسم كل يوم. ماذا يجب أن تضيف أيضًا؟

تدريب

بيئة تجريب الكود

اجعل رسم الجدول رخيصًا

ثلاثة إصلاحات للأداء، كلها في CSS:

  1. أعد كتابة الـ keyframes toast-in لينزلق الإشعار من اليسار باستخدام خصائص ملائمة للـ compositor فقط (translate أو transform، إضافةً إلى opacity إن شئت). أبقِ الاسم toast-in، وتأكّد من أن الإشعار ينتهي عند left: 16px.
  2. دع المتصفّح يتخطّى رسم أقسام .day الواقعة خارج الشاشة، مع تقدير للحجم الذاتي يستخدم auto وطولًا.
  3. اجعل إطار .photo مربّعًا قبل تحميل أيّ صورة، ليحجز مساحته.
<section class="day">
  <h2>Day 1</h2>
  <article class="speaker">
    <div class="photo" role="img" aria-label="Ana Ortiz"></div>
    <p>Ana Ortiz</p>
  </article>
</section>
<section class="day">
  <h2>Day 2</h2>
  <p>Twenty-eight sessions…</p>
</section>
<div class="toast" role="status">Saved to your schedule</div>جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • toast-in لا تحرّك إلا translate/transform وopacity
  • الإشعار ما زال يشغّل حركة toast-in
  • الإشعار ينتهي في موضعه الطبيعي
  • أقسام الأيام تستخدم content-visibility: auto
  • لأقسام الأيام تقدير للحجم الذاتي مع auto
  • إطار الصورة مربّع قبل تحميل الصورة