Houdini - إزالة الغموض عن CSS

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

مرحبًا بك في Houdini.

يتألف فريق عمل Houdini من مهندسين من Mozilla وApple وOpera وMicrosoft وHP وIntel وGoogle يعملون معًا لعرض أجزاء معيّنة من محرّك CSS لمطوّري الويب. تعمل المجموعة المعنيّة على مجموعة من مشاريع التطوير بهدف أن تقبلها W3C لتصبح معايير ويب فعلية. وضعوا لأنفسهم بعض الأهداف العالية المستوى، وحوّلوها إلى drafts مواصفات أدّت بدورها إلى إنشاء مجموعة من drafts مواصفات داعمة ذات مستوى أقل.

وعادةً ما يُقصد بمصطلح "Houdini" مجموعة المسودات هذه. في وقت كتابة هذه المقالة، كانت قائمة المسودات غير مكتملة وكانت بعض المسودات مجرد عناصر نائبة.

المواصفات

وحدات العمل (المواصفات)

لا تكون وحدات العمل مفيدة بحد ذاتها. وهي مفهوم تم تقديمه لتوفير إمكانية استخدام العديد من المسودات اللاحقة. إذا فكّرت في Web Workers عند قراءة "worklet"، فأنت على صواب. هناك تداخل كبير بين المفهومَين. فلماذا نحتاج إلى شيء جديد عندما لدينا عمال؟

يهدف Houdini إلى توفير واجهات برمجة تطبيقات جديدة للسماح لمطوّري الويب بربط رموزهم البرمجية بمحرك CSS والأنظمة المحيطة به. من المرجّح أن يكون من المنطقي افتراض أنّه يجب تشغيل بعض مقتطفات الرمز البرمجي هذه في كل لقطة فردية. ويجب أن يكون بعضها محدّدًا بحكم التعريف. مقتطف من مواصفات Web Worker:

وهذا يعني أنّ عمال الويب غير فعّالين لتنفيذ الإجراءات التي تخطّط Houdini لتنفيذها. لذلك، تمّ اختراع وحدات العمل. تستخدِم وحدات العمل فئات ES2015 لتحديد مجموعة من الطرق التي يتم تحديد توقيعاتها مسبقًا من قِبل نوع وحدة العمل. وهي خفيفة وقصيرة الأجل.

واجهة CSS Paint API (المواصفات)

تكون واجهة برمجة التطبيقات Paint API مفعّلة تلقائيًا في الإصدار 65 من Chrome. يُرجى قراءة المقدّمة التفصيلية.

أداة تركيب الصور

واجهة برمجة التطبيقات الموضّحة هنا قديمة. تمّت إعادة تصميم أداة "عملية تركيب" وأصبح اسمها المقترَح الآن هو "عملية إنشاء رسوم متحركة". يمكنك الاطّلاع على مزيد من المعلومات حول الإصدار الحالي من واجهة برمجة التطبيقات.

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

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

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

أداة تركيب.

كما يوحي الاسم، تتيح لك أداة "عملية التركيب" الربط بوحدة التركيب والتأثير في طريقة وضع طبقة العنصر التي سبق أن تم رسمها وترتيبها فوق الطبقات الأخرى.

للحصول على مزيد من تحديد ، يمكنك إخبار المتصفّح بأنّك تريد الربط بعملية compositing لعقدة DOM معيّنة، ويمكنك طلب الوصول إلى سمات معيّنة مثل موضع التمرير أو transform أو opacity. يؤدي ذلك إلى فرض هذا العنصر على طبقته الخاصة وفي كل إطار يتم استدعاء الرمز. يمكنك نقل الطبقة من خلال التلاعب بتحويل الطبقات وتغيير سماتها (مثل opacity) ما يتيح لك تنفيذ إجراءات رائعة بمعدّل 60 لقطة في الثانية.

في ما يلي عملية تنفيذ كاملة لميزة التمرير البانورامي باستخدام ملف معالجة الصور المصغرة.

// main.js
window.compositorWorklet.import('worklet.js')
    .then(function() {
    var animator = new CompositorAnimator('parallax');
    animator.postMessage([
        new CompositorProxy($('.scroller'), ['scrollTop']),
        new CompositorProxy($('.parallax'), ['transform']),
    ]);
    });

// worklet.js
registerCompositorAnimator('parallax', class {
    tick(timestamp) {
    var t = self.parallax.transform;
    t.m42 = -0.1 * self.scroller.scrollTop;
    self.parallax.transform = t;
    }

    onmessage(e) {
    self.scroller = e.data[0];
    self.parallax = e.data[1];
    };
});

كتب "روبرت فلاك" polyfill لتطبيق worklet الخاص بالمركب حتى تتمكّن من تجربته، ومن الواضح أنّه سيؤثّر في الأداء بشكلٍ أكبر.

أداة عمل التنسيق (المواصفات)

تم اقتراح أول مسودة حقيقية للمواصفات. لن يتم تنفيذ هذه الميزة إلا بعد فترة طويلة.

مرة أخرى، تكون المواصفات لهذه الميزة فارغة عمليًا، ولكن المفهوم هو مثير للاهتمام: يمكنك كتابة التنسيق الخاص بك. من المفترض أن تتيح لك أداة تصميم الشبكة تنفيذ display: layout('myLayout') وتشغيل JavaScript لترتيب العناصر الفرعية للعقدة في مربّع العقدة.

بالطبع، إنّ تنفيذ تنسيق flex-box في CSS باستخدام JavaScript بالكامل يكون أبطأ من تنفيذ تنسيق مكافئ مضمّن، ولكن من السهل تخيل سيناريو يمكن فيه تحقيق مكاسب في الأداء من خلال تقليل الوقت والمجهود. تخيل موقعًا إلكترونيًا يتألف من مربّعات فقط، مثل Windows 10 أو تنسيق مُعدّ للعرض على شاشة عريضة. لا يتم استخدام موضع العنصر المطلق أو الثابت، ولا يتم استخدام z-index، ولا تتراكب العناصر أبدًا أو يكون لها أي نوع من الحدود أو الفائض. يمكن أن يؤدي التمكّن من تخطّي كل عمليات التحقّق هذه عند إعادة التنسيق إلى تحسين الأداء.

registerLayout('random-layout', class {
    static get inputProperties() {
        return [];
    }
    static get childrenInputProperties() {
        return [];
    }
    layout(children, constraintSpace, styleMap) {
        const width = constraintSpace.width;
        const height = constraintSpace.height;
        for (let child of children) {
            const x = Math.random()*width;
            const y = Math.random()*height;
            const constraintSubSpace = new ConstraintSpace();
            constraintSubSpace.width = width-x;
            constraintSubSpace.height = height-y;
            const childFragment = child.doLayout(constraintSubSpace);
            childFragment.x = x;
            childFragment.y = y;
        }

        return {
            minContent: 0,
            maxContent: 0,
            width: width,
            height: height,
            fragments: [],
            unPositionedChildren: [],
            breakToken: null
        };
    }
});

نموذج CSSOM المكتوب (المواصفات)

يعالج نموذج CSSOM (نموذج عناصر CSS أو نموذج عناصر أوراق الأنماط المتتالية) مشكلة ربما واجهناها جميعًا وتعلّمنا التعامل معها. سأوضّح ذلك باستخدام سطر من JavaScript:

    $('#someDiv').style.height = getRandomInt() + 'px';

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

هذه المسودة هي إحدى المسودات الأكثر نضجًا، ويتم حاليًا تطوير polyfill. (إخلاء مسؤولية: سيؤدي استخدام الpolyfill بوضوح إلى زيادة التكلفة الحسابية. والهدف من ذلك هو توضيح مدى سهولة استخدام واجهة برمجة التطبيقات.)

بدلاً من السلاسل، ستتعامل مع StylePropertyMap للعنصر، حيث تمتلك كل سمة CSS مفتاحها الخاص بها ونوع القيمة المقابل. إنّ السمات مثل width لها LengthValue كنوع القيمة. LengthValue هو قاموس لجميع وحدات CSS، مثل em وrem وpx وpercent وما إلى ذلك. سيؤدي ضبط height: calc(5px + 5%) إلى الحصول على LengthValue{px: 5, percent: 5}. لا تقبل بعض السمات، مثل box-sizing، سوى كلمات رئيسية معيّنة، وبالتالي يكون لها نوع قيمة KeywordValue. ويمكن بعد ذلك التحقّق من صحة هذه السمات أثناء التشغيل.

<div style="width: 200px;" id="div1"></div>
<div style="width: 300px;" id="div2"></div>
<div id="div3"></div>
<div style="margin-left: calc(5em + 50%);" id="div4"></div>
var w1 = $('#div1').styleMap.get('width');
var w2 = $('#div2').styleMap.get('width');
$('#div3').styleMap.set('background-size',
    [new SimpleLength(200, 'px'), w1.add(w2)])
$('#div4')).styleMap.get('margin-left')
    // => {em: 5, percent: 50}

السمات والقيم

(المواصفات)

هل لديك معرفة بـ "السمات المخصّصة لتنسيق CSS" (أو الاسم المعرِّف غير الرسمي لها "متغيّرات CSS")؟ هذه هي العناصر نفسها ولكن مع أنواع. حتى الآن، كان بإمكان المتغيّرات استخدام قيم سلاسل فقط وكانت تتم معالجة هذه القيم باستخدام أسلوب البحث والاستبدال البسيط. ستسمح لك هذه المسودة ليس فقط بتحديد نوع للمتغيّرات، ولكن أيضًا بتحديد قيمة تلقائية والتأثير في سلوك اكتساب السمات باستخدام واجهة برمجة تطبيقات JavaScript. من الناحية الفنية، سيسمح ذلك أيضًا باستخدام خصائص مخصّصة متحركة باستخدام عمليات النقل والصور المتحركة العادية في CSS، وهو أمر قيد المراجعة أيضًا.

["--scale-x", "--scale-y"].forEach(function(name) {
document.registerProperty({
    name: name,
    syntax: "<number>",
    inherits: false,
    initialValue: "1"
    });
});

مقاييس الخطوط

مقاييس الخطوط هي كما يبدو من اسمها. ما هو مربّع الحدود (أو مربّعات الحدود) عند عرض السلسلة X بالخط Y بالحجم Z؟ ماذا يحدث إذا استخدمت تعليقات توضيحية بتنسيق Ruby؟ لقد تلقّينا الكثير من الطلبات بشأن هذا الأمر، ومن المفترض أن يلبّي Houdini هذه الرغبات أخيرًا.

انتظر، فهناك المزيد.

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

إصدارات تجريبية

لقد جعلت رمز العرض التجريبي مفتوح المصدر (العرض التجريبي المباشر باستخدام polyfill).