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

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

הזן את הודיני!

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

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

המפרט

worklet (spec)

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

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

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

CSS Paint API (spec)

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

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

ה-API שמתואר כאן יצא משימוש. ה-worklet של הקומפוזיטור קיבל עיצוב מחדש, ועכשיו הוא מוצע בתור 'אנימציה של worklet'. מידע נוסף על הגרסה הנוכחית של ה-API

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

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

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

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

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

worklet של הפריסה (spec)

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

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

Typed CSSOM (מודל CSS Object או מודל מדורג של גיליונות סגנון) מטפל בבעיה שכנראה כולנו נתקלו בה ולמדנו פשוט להשלים אותה. אני רוצה להדגים באמצעות שורת 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? התבקשנו הרבה בקשות, ולכן הודיני צריך סוף סוף להגשים את המשאלות האלה.

רגע, יש עוד!

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

הדגמות

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