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

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

קבלו את Houdini!

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

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

המפרטים

רכיבי Worklet (מפרט)

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

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

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

CSS Paint API (מפרט)

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

worklet של Compositor

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

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

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

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

מודול worklet של רכיב עיבוד תמונה.

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

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

worklet של פריסה (מפרט)

הוצעה טיוטה ראשונה של המפרט. ההטמעה תתבצע רק בעוד זמן מה.

שוב, המפרט לכך כמעט ריק, אבל הקונספט מעניין: כותבים את הפריסה שלכם! ה-worklet של הפריסה אמור לאפשר לכם לבצע את הפעולה 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 ממוין (מפרט)

Typed 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 אמור להגשים את הבקשות האלה.

רגע, יש עוד!

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

הדגמות

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

אלא אם צוין אחרת, התוכן של דף זה הוא ברישיון Creative Commons Attribution 4.0 ודוגמאות הקוד הן ברישיון Apache 2.0. לפרטים, ניתן לעיין במדיניות האתר Google Developers‏.‏ Java הוא סימן מסחרי רשום של חברת Oracle ו/או של השותפים העצמאיים שלה.

עדכון אחרון: 2020-07-19 (שעון UTC).