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

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

استكشِف Houdini.

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

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

المواصفات

الوظائف الصغيرة (spec)

الوظائف الصغيرة في حد ذاتها ليست مفيدة حقًا. إنها مفهوم تم تقديمه لجعل العديد من المسودات اللاحقة ممكنة. إذا فكرت في عمال الويب عند قراءة "worklet"، فأنت مخطئ. لديهم الكثير من التداخل المفاهيمي. فلماذا يظهر شيء جديد عندما لدينا عمال بالفعل؟

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

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

CSS Paint API (spec)

يتم تفعيل Paint API تلقائيًا في Chrome 65. اقرأ المقدمة التفصيلية.

الوظيفة المصغّرة للمكوّن

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

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

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

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

الوظيفة المصغّرة للمكوّن

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

للحصول على معلومات أكثر تحديدًا، يمكنك إعلام المتصفّح بأنّك تريد إضافة جزء من عملية الإنشاء إلى عقدة 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 للمكوّن الإضافي لتتمكن من تجربتها، الأمر الذي يكون له تأثير أعلى بكثير في الأداء.

وظيفة التنسيق الصغيرة (spec)

وتم اقتراح أول مسودة لمواصفات حقيقية. التنفيذ جيد لفترة طويلة.

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

إنّ تنفيذ JavaScript بشكل كامل لتنسيق flex-box في CSS يكون أبطأ من تنفيذ تنفيذ أصلي مكافئ، ولكن من السهل تصوّر سيناريو يمكن فيه لقص الزوايا تحقيق أداء أفضل. تخيل أن موقعًا إلكترونيًا لا يتكون من أي شيء سوى مربّعات، مثل 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 المكتوبة (spec)

تعالج 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}

الخصائص والقيم

(spec)

هل تعرف خصائص 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، لكن مستقبل هذه الأشياء غير مؤكد إلى حد ما وهي ليست أكثر من عناصر نائبة للأفكار. ومن الأمثلة على ذلك سلوكيات التجاوز المخصص، وواجهة برمجة التطبيقات لإضافة بنية CSS، وإضافة سلوك التمرير الأصلي، وغيرها من الأمور الطموحة التي تتيح جميع الأمور على النظام الأساسي للويب لم تكن متاحة من قبل.

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

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