Houdini – הסרת מסתורי CSS

חשבתם פעם על כמות העבודה ששירות CSS עובד בה? כשמשנים מאפיין יחיד ופתאום כל האתר מוצג בפריסה שונה. זה סוג של קסם. עד עכשיו, קהילת מפתחי האתרים – יכולנו רק לראות את הקסם ולצפות בו. מה אם אנחנו רוצים להמציא את הקסם שלנו? מה אם אנחנו רוצים להפוך לקוסם?

הזן Houdini!

צוות המשימה של Houdini מורכב ממהנדסים מ-Mozilla, Apple, Opera , Microsoft, HP, Intel ו-Google שעובדים ביחד כדי לחשוף חלקים מסוימים במנוע ה-CSS למפתחי אינטרנט. צוות המשימה עובד על אוסף של טיוטות במטרה לקבל אותן ב-W3C כך שיהפכו לסטנדרטים בפועל באינטרנט. הם הציבו לעצמם כמה יעדים ברמה גבוהה, והפכו אותם לטיוטות של מפרט. כתוצאה מכך, נוצרה קבוצה של טיוטות מפרטים תומכות ברמה נמוכה יותר.

אוסף הטיוטות האלה הוא מה שמתכוון בדרך כלל כשמישהו מדבר על "הודו". נכון לעכשיו, רשימת הטיוטות אינה מלאה וחלק מהטיוטות הן רק placeholders.

המפרטים

Worklets (spec)

אין ממש תועלת ב-Worklets כשלעצמם. הם קונספט שנוצר כדי לאפשר יצירה של רבות מהטיוטות שבהמשך. אם חשבתם על Web Workers כשקראתם את worklet, אינכם טועים. יש בהם הרבה חפיפה רעיונית. אז למה דבר חדש כשכבר יש לנו עובדים?

המטרה של Houdini היא לחשוף ממשקי API חדשים כדי לאפשר למפתחי אתרים להוסיף קוד משלהם למנוע ה-CSS ולמערכות הסובבות. ההנחה היא שחלק ממקטעי הקוד האלה יצטרכו להריץ כל פריים בודד. חלק מהם חייבים לפי הגדרה. ציטוט של המפרט של Web Worker:

כלומר, עובדי אינטרנט לא יכולים לממש את מה ש-Huudini מתכננת לעשות. לכן, הומצאו worklet. ב-Worklets משתמשים במחלקות ES2015 כדי להגדיר אוסף של שיטות, שהחתימות שלהן מוגדרות מראש לפי סוג ה-worklet. הם קלים ותואמים לטווח קצר.

CSS Paint API (spec)

Paint API מופעל כברירת מחדל ב-Chrome 65. קראו את המבוא המפורט.

worklet של הרכבים

ה-API שמתואר כאן מיושן. העבודה המרכיבה עוצבה מחדש ומוצעת עכשיו כ-Animation Worklet. אפשר לקרוא מידע נוסף על איטרציה נוכחית של ה-API.

למרות שמפרט העבודה המרוכבת הועבר ל-WICG וייכנס אליו שוב, זה המפרט שהכי מרגש אותי. חלק מהפעולות מתבצעות במיקור חוץ לכרטיס הגרפי של המחשב על ידי מנוע ה-CSS, למרות שהדבר תלוי גם בכרטיס הגרפי וגם במכשיר באופן כללי.

בדרך כלל הדפדפן משתמש בעץ ה-DOM, ולכן על סמך קריטריונים ספציפיים הוא מחליט אם לחלק מהסתעפים ותתי-עצים מסוימים שכבה משלהם. תתי-העצים האלה צובעים את עצמם מעליו (יכול להיות שבעתיד תשתמשו בעבודת צבע). בשלב האחרון, כל השכבות הנפרדות האלו, שעכשיו נצבעו, מוערמות וממוקמות זו על גבי זו, בהתאם לאנדיקציות z, להמרות תלת-ממדיות וכד', כדי ליצור את התמונה הסופית שמוצגת במסך. התהליך הזה נקרא compositing, והוא מבוצע על ידי המחבר.

היתרון של תהליך החיבור הוא שאין צורך לגרום לכל הרכיבים לצבוע את עצמם מחדש כשהדף גולל קצת. במקום זאת, תוכלו להשתמש שוב בשכבות מהפריים הקודם ורק להריץ מחדש את הקומפוזיטור עם מיקום הגלילה המעודכן. כך הכול מהיר. כך אנחנו יכולים להגיע ל-60fps.

worklet של קומפוזיציה.

כפי שמרמז השם, פעולת הקומפוסטרציה מאפשרת לכם להתחבר למרכיב הזה ולהשפיע על האופן שבו שכבת הרכיב, שכבר נצבעה, ממוקמת ושכבות מעל השכבות האחרות.

כדי להיות יותר ספציפיים, אפשר להגדיר לדפדפן שברצונך להתחבר לתהליך היצירה של צומת DOM מסוים ולבקש גישה למאפיינים מסוימים כמו מיקום הגלילה, transform או opacity. פעולה זו מאלצת את הרכיב לעבור לשכבה שלו ובכל מסגרת, הקוד שלכם נקרא. ניתן להזיז את השכבה באמצעות מניפולציה של השכבות ולשנות את המאפיינים שלה (למשל opacity), וכך לבצע פעולות מפוארות בשנייה בקצב אדיר של 60fps.

זו הטמעה מלאה של גלילת פרלקס באמצעות worklet של קומפוזיטור.

// 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 של פריסה (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 יוסיף כמובן עוד תקורה מחושבת. המטרה היא להראות עד כמה ה-API נוח.)

במקום מחרוזות, תעבדו על 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 API. מבחינה טכנית, הדבר יאפשר גם ליצור אנימציה של מאפיינים מותאמים אישית עם מעברים רגילים של CSS ואנימציות, שגם הן מובחנות.

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

מדדי הגופן

מדדי גופנים בדיוק כפי שהם נשמעים. מהי התיבה התוחמת (או התיבות התוחמות) כשמעבדים את מחרוזת X עם גופן Y בגודל Z? מה יקרה אם אשתמש בהערות Ruby? כבר הרבה ביקשו את זה והודיני צריך סוף סוף להגשים את המשאלות האלה.

רגע, יש עוד!

ברשימת הטיוטות של Houdini יש מפרטים רבים יותר, אבל העתיד כבר לא בטוח ולא רק לגבי רעיונות לדוגמה. לדוגמה, התנהגויות גלישה מותאמות אישית, API של תוסף תחביר CSS, תוסף להתנהגות גלילה מקומית ודברים שאפתניים דומים שמאפשרים בפלטפורמת האינטרנט לבצע פעולות שלא היו אפשריות קודם לכן.

הדגמות

יש לי קוד פתוח של הקוד להדגמה (הדגמה פעילה באמצעות polyfill).