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

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

الدخول إلى "هوديني"

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

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

المواصفات

Worklets (spec)

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

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

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

CSS Paint API (spec)

يتم تفعيل واجهة برمجة التطبيقات Paint API تلقائيًا في الإصدار Chrome 65. اقرأ المقدمة التفصيلية.

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

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

على الرغم من نقل مواصفات أداة العمل إلى 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؟ ماذا لو استخدمت تعليقات Rubby التوضيحية؟ وقد تم طلب ذلك كثيرًا، ويجب على "هوديني" أخيرًا أن يجعل هذه الأمنيات حقيقة.

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

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

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

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