تحريك تمويه

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

TL;DR

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

ومع ذلك، يمكن أن تكون هذه التقنية مزعجة جدًا عند تطبيقها بدون أي فترة انتقالية. يبدو أنّ تحريك التمويه، أي الانتقال من صورة غير مموّهة إلى صورة مموّهة، هو خيار معقول، ولكن إذا سبق لك تجربة ذلك على الويب، من المحتمل أنّك لاحظت أنّ الرسوم المتحركة ليست سلسة على الإطلاق، كما يوضّح هذا العرض التوضيحي إذا لم يكن لديك جهاز قوي. هل يمكننا تقديم أداء أفضل؟

المشكلة

تحوّل وحدة المعالجة المركزية (CPU) الترميز إلى مواد. يتم تحميل الزخارف إلى وحدة معالجة الرسومات (GPU). ترسم وحدة معالجة الرسومات هذه الأنسجة في مخزن الإطارات باستخدام برامج التظليل. يتم التشويش في
المظلّل.

في الوقت الحالي، لا يمكننا جعل تأثير التمويه المتحرّك يعمل بكفاءة. ومع ذلك، يمكننا إيجاد حل بديل يبدو جيدًا بما فيه الكفاية، ولكنّه ليس، من الناحية الفنية، تمويهًا متحركًا. للبدء، دعونا أولاً نفهم سبب بطء التمويه المتحرّك. لتشويش العناصر على الويب، هناك أسلوبان: السمة filter في CSS وفلاتر SVG. وبفضل زيادة التوافق وسهولة الاستخدام، يتم عادةً استخدام فلاتر CSS. إذا كان عليك توفير إمكانية استخدام Internet Explorer، ليس أمامك خيار سوى استخدام فلاتر SVG لأنّ الإصدارَين 10 و11 من Internet Explorer يتيحان استخدامها، ولكن لا يتيحان استخدام فلاتر CSS. والخبر السار هو أنّ الحل البديل الذي نقدّمه لتحريك التمويه يعمل مع كلتا الطريقتين. لذلك، لنحاول تحديد المشكلة من خلال فحص "أدوات مطوّري البرامج".

إذا فعّلت الخيار "تمييز اللوحات الوامضة" في "أدوات مطوّري البرامج"، لن تظهر أي ومضات على الإطلاق. يبدو أنّه لا تتم إعادة طلاء أي عناصر. وهذا صحيح من الناحية الفنية، لأنّ "إعادة الطلاء" تشير إلى أنّ وحدة المعالجة المركزية (CPU) يجب أن تعيد طلاء نسيج عنصر تمت ترقيته. عندما يتم تمييز عنصر وتشويشه في الوقت نفسه، يتم تطبيق التشويش من خلال وحدة معالجة الرسومات باستخدام برنامج تظليل.

تستخدم فلاتر SVG وفلاتر CSS فلاتر الالتفاف لتطبيق تأثير التمويه. تُعدّ فلاتر الالتفاف باهظة التكلفة إلى حدّ ما لأنّه يجب أخذ عدد من وحدات البكسل المدخلة في الاعتبار لكل وحدة بكسل مخرَجة. وكلما زاد حجم الصورة أو نصف قطر التمويه، زادت تكلفة التأثير.

وهنا تكمن المشكلة، فنحن ننفّذ عملية وحدة معالجة الرسومات (GPU) باهظة التكلفة في كل لقطة، ما يؤدي إلى تجاوز ميزانية اللقطة البالغة 16 ملي ثانية، وبالتالي ينتهي بنا الأمر إلى أقل بكثير من 60 لقطة في الثانية.

الوقوع في حفرة الأرانب

إذًا، ما الذي يمكننا فعله لضمان سير هذه العملية بسلاسة؟ يمكننا استخدام خفة اليد! بدلاً من تحريك قيمة التمويه الفعلية (نصف قطر التمويه)، نحسب مسبقًا نسختَين مموّهتَين تزداد فيهما قيمة التمويه بشكل كبير، ثم ننتقل بينهما بسلاسة باستخدام opacity.

التلاشي التدريجي هو سلسلة من عمليات التلاشي التدريجي المتداخلة للظهور والاختفاء. إذا كان لدينا أربع مراحل تمويه مثلاً، سنخفّف المرحلة الأولى تدريجيًا مع زيادة المرحلة الثانية تدريجيًا في الوقت نفسه. بعد أن تصل المرحلة الثانية إلى مستوى عتامة 100% وتصل المرحلة الأولى إلى 0%، نخفّف عتامة المرحلة الثانية مع زيادة عتامة المرحلة الثالثة. بعد الانتهاء من ذلك، ننتقل أخيرًا إلى المرحلة الثالثة ونعرض النسخة الرابعة والأخيرة. في هذا السيناريو، تستغرق كل مرحلة ربع المدة الإجمالية المطلوبة. من الناحية المرئية، يبدو هذا التأثير مشابهًا جدًا لتأثير التمويه الحقيقي المتحرّك.

في تجاربنا، أدّى زيادة نصف قطر التمويه بشكل كبير في كل مرحلة إلى تحقيق أفضل النتائج المرئية. مثال: إذا كان لدينا أربع مراحل تمويه، سنطبّق filter: blur(2^n) على كل مرحلة، أي المرحلة 0: 1 بكسل، والمرحلة 1: 2 بكسل، والمرحلة 2: 4 بكسل، والمرحلة 3: 8 بكسل. إذا فرضنا كل نسخة من هذه النسخ المموهة على طبقة خاصة بها (تُعرف باسم "الترويج") باستخدام will-change: transform، من المفترض أن يكون تغيير مستوى الشفافية في هذه العناصر سريعًا جدًا. من الناحية النظرية، سيسمح لنا ذلك بتحديد أولويات العمل المكلف المتعلق بالتمويه. تبيّن أنّ المنطق معيب. إذا شغّلت هذا العرض التوضيحي، ستلاحظ أنّ معدّل عرض اللقطات في الثانية لا يزال أقل من 60 لقطة في الثانية، وأنّ التشويش أصبح أسوأ من ذي قبل.

تعرض "أدوات مطوّري البرامج" تتبُّعًا يوضّح أنّ وحدة معالجة الرسومات كانت مشغولة لفترات طويلة.

يُظهر فحص سريع لأدوات مطوّري البرامج أنّ وحدة معالجة الرسومات لا تزال مشغولة للغاية، وأنّها تمدّد كل لقطة إلى حوالي 90 ملي ثانية. ولكن لماذا؟ لم نعد نغيّر قيمة التمويه، بل الشفافية فقط، فما الذي يحدث؟ تكمن المشكلة، مرة أخرى، في طبيعة تأثير التمويه: كما سبق أن شرحنا، إذا تم الترويج للعنصر وتمويهه في الوقت نفسه، سيتم تطبيق التأثير من خلال وحدة معالجة الرسومات. لذلك، على الرغم من أنّنا لم نعد نحرّك قيمة التمويه، إلا أنّ الملمس نفسه لا يزال غير مموّه ويجب أن تعيد وحدة معالجة الرسومات تمويهه في كل إطار. والسبب في انخفاض معدّل عرض اللقطات بشكل أكبر من السابق هو أنّ وحدة معالجة الرسومات (GPU) لديها عمل أكثر من السابق، إذ يظهر في معظم الأوقات نسيجان يجب تمويههما بشكل مستقل.

والنتيجة ليست جميلة، ولكنّها تجعل الرسوم المتحركة سريعة جدًا. نعود إلى عدم الترويج للعنصر الذي سيتم تمويهه، ولكننا نروّج بدلاً من ذلك لعنصر تضمين رئيسي. إذا تم تمويه عنصر وترقيته في الوقت نفسه، سيتم تطبيق التأثير من خلال وحدة معالجة الرسومات. وهذا ما أدى إلى بطء العرض التوضيحي. إذا كان العنصر غير واضح ولكن لم تتم ترقيته، سيتم تحويل التشويش إلى أقرب نسيج رئيسي بدلاً من ذلك. في حالتنا، هذا هو العنصر الرئيسي المروّج له. أصبحت الصورة المموهة الآن هي نسيج العنصر الأصل ويمكن إعادة استخدامها في جميع اللقطات المستقبلية. لا يمكننا الاستفادة من هذه الطريقة إلا إذا علمنا أنّ العناصر المموهة ليست متحركة وأنّ تخزينها مؤقتًا مفيد. إليك عرضًا توضيحيًا يطبّق هذه التقنية. يا تُرى ما رأي هاتف Moto G4 في هذا النهج؟ تنبيه بكشف تفاصيل عن الحبكة: يعتقد أنّها رائعة:

تعرض "أدوات مطوّري البرامج" تتبُّعًا يتضمّن الكثير من وقت الخمول لوحدة معالجة الرسومات.

أصبح لدينا الآن الكثير من المساحة المتاحة على وحدة معالجة الرسومات ومعدّل 60 إطارًا في الثانية سلسًا للغاية. لقد نجحنا!

تحويل النموذج إلى منتج

في العرض التوضيحي، ضاعفنا بنية DOM عدة مرات للحصول على نُسخ من المحتوى لتطبيق التمويه بدرجات مختلفة. قد تتساءل عن كيفية عمل ذلك في بيئة إنتاجية، إذ قد يؤدي إلى بعض الآثار الجانبية غير المقصودة مع أنماط CSS الخاصة بالمؤلف أو حتى JavaScript. إجابتك صحيحة. Enter Shadow DOM!

في حين أنّ معظم الأشخاص يعتبرون Shadow DOM طريقة لإرفاق عناصر "داخلية" بالعناصر المخصّصة، إلا أنّها أيضًا أداة أساسية للعزل والأداء. لا يمكن لملفات JavaScript وCSS اختراق حدود Shadow DOM، ما يتيح لنا تكرار المحتوى بدون التدخّل في أنماط المطوّر أو منطق التطبيق. لدينا حاليًا عنصر <div> لكل نسخة يتم تحويلها إلى صورة نقطية، ونستخدم الآن عناصر <div> هذه كمضيفات للظلال. ننشئ ShadowRoot باستخدام attachShadow({mode: 'closed'}) ونرفق نسخة من المحتوى بـ ShadowRoot بدلاً من <div> نفسه. علينا أيضًا التأكّد من نسخ جميع أوراق الأنماط إلى ShadowRoot لضمان أن تكون النسخ منمّقة بالطريقة نفسها التي تم بها تنميق النسخة الأصلية.

لا تتوافق بعض المتصفّحات مع الإصدار 1 من Shadow DOM، وفي هذه الحالة، نلجأ إلى تكرار المحتوى فقط ونأمل ألا يحدث أي خطأ. يمكننا استخدام Shadow DOM polyfill مع ShadyCSS، ولكن لم ننفّذ ذلك في مكتبتنا.

وهكذا نكون قد انتهينا من رحلتنا في مسار عرض Chrome، وتمكّنا من معرفة كيفية تحريك التمويهات بكفاءة على جميع المتصفّحات.

الخاتمة

يجب عدم الاستخفاف بهذا النوع من التأثيرات. بما أنّنا ننسخ عناصر DOM ونفرضها على طبقتها الخاصة، يمكننا تجاوز حدود الأجهزة المنخفضة المواصفات. يُعدّ نسخ جميع أوراق الأنماط إلى كل ShadowRoot من المخاطر المحتملة على الأداء أيضًا، لذا عليك تحديد ما إذا كنت تفضّل تعديل منطقك وأنماطك كي لا تتأثر بالنسخ في LightDOM أو استخدام أسلوب ShadowDOM. ولكن في بعض الأحيان، قد يكون أسلوبنا استثمارًا مهمًا. يمكنك الاطّلاع على الرمز في مستودع GitHub بالإضافة إلى العرض التوضيحي ويمكنك التواصل معي على Twitter إذا كانت لديك أي أسئلة.