Houdini - CSS 이해하기

CSS가 하는 작업의 양을 생각해 본 적이 있나요? 단일 속성을 변경한 후 갑자기 전체 웹사이트가 다른 레이아웃으로 표시됩니다. 마법과도 같은 기적입니다. 지금까지 웹 개발자 커뮤니티는 이 마법을 지켜보고 관찰할 수만 있었습니다. 나만의 마법을 만들고 싶다면 어떻게 해야 하나요? 마술사가 되고 싶다면 어떻게 해야 할까요?

Houdini를 시작하세요.

Houdini 태스크 포스는 Mozilla, Apple, Opera, Microsoft, HP, Intel, Google의 엔지니어로 구성되어 CSS 엔진의 특정 부분을 웹 개발자에게 노출합니다. 이 태스크포스는 W3C에서 실제 웹 표준이 될 수 있도록 초안 모음을 만들고 있습니다. 몇 가지 대략적인 목표를 세우고 이를 사양 초안으로 전환하여 하위 수준의 지원 사양 초안을 만들었습니다.

이러한 초안 모음을 일반적으로 'Houdini'라고 합니다. 이 글을 작성하는 시점에서 초안 목록은 불완전하며 일부 초안은 자리표시자일 뿐입니다.

사양

워크렛 (사양)

워크렛 자체는 그다지 유용하지 않습니다. 이는 이후 초안을 많이 만들 수 있도록 도입된 개념입니다. 'worklet'을 읽을 때 Web Workers를 생각했다면 틀린 것이 아닙니다. 이들은 개념적으로 많은 부분이 겹칩니다. 이미 인력이 있는데 왜 새로운 방법을 도입해야 하나요?

Houdini의 목표는 웹 개발자가 자체 코드를 CSS 엔진 및 주변 시스템에 연결할 수 있는 새 API를 노출하는 것입니다. 이러한 코드 프래그먼트 중 일부를 every. single. frame 실행해야 한다고 가정하는 것은 비현실적일 수 있습니다. 그중 일부는 정의상 반드시 웹 작업자 사양을 인용합니다.

이는 웹 근로자들이 Houdini가 계획하는 일을 할 수 없다는 것을 의미합니다. 따라서 워크렛이 발명되었습니다. 워크렛은 ES2015 클래스를 사용하여 메서드 모음을 정의하며, 이 모음의 서명은 워크렛 유형에 의해 사전 정의됩니다. 가볍고 수명이 짧습니다.

CSS Paint API (사양)

Paint API는 Chrome 65에서 기본적으로 사용 설정됩니다. 자세한 소개를 읽어보세요.

컴포저 작업자

여기에 설명된 API는 더 이상 사용되지 않습니다. 컴포저이터 워크렛이 새롭게 설계되었으며 이제 '애니메이션 워크렛'으로 제안됩니다. 현재 API 버전에 대해 자세히 알아보세요.

컴포지터 워크렛 사양이 WICG로 이동되어 계속 반복될 예정이지만, 저에게 가장 흥미로운 사양입니다. 일반적으로 그래픽 카드와 기기 모두에 따라 다르지만 일부 작업은 CSS 엔진에 의해 컴퓨터의 그래픽 카드로 아웃소싱됩니다.

브라우저는 일반적으로 DOM 트리를 가져와 특정 기준에 따라 일부 브랜치와 하위 트리에 자체 레이어를 제공하기로 결정합니다. 이러한 하위 트리는 나중에 페인트 워크렛을 사용하여 자체적으로 페인트됩니다. 마지막 단계로, 이제 페인팅된 이러한 모든 개별 레이어가 z 인덱스, 3D 변환 등을 고려하여 서로 겹쳐 배치되어 화면에 표시되는 최종 이미지를 생성합니다. 이 프로세스를 합성이라고 하며 컴포저이터가 실행합니다.

합성 프로세스의 장점은 페이지를 약간 스크롤할 때 모든 요소를 다시 페인트할 필요가 없다는 것입니다. 대신 이전 프레임의 레이어를 재사용하고 업데이트된 스크롤 위치로 컴포지터를 다시 실행하면 됩니다. 이렇게 하면 작업 속도가 빨라집니다. 이렇게 하면 60fps에 도달할 수 있습니다.

컴포지터 워크렛.

이름에서 알 수 있듯이 컴포저 작업 덕분에 컴포저에 후크를 걸고 이미 페인팅된 요소의 레이어가 다른 레이어 위에 배치되고 중첩되는 방식에 영향을 줄 수 있습니다.

좀 더 구체적으로 말하자면 브라우저에 특정 DOM 노드의 컴포지션 프로세스에 후크하려고 한다고 알리고 스크롤 위치, transform 또는 opacity와 같은 특정 속성에 대한 액세스를 요청할 수 있습니다. 이렇게 하면 이 요소가 자체 레이어로 강제되고 각 프레임에서 코드가 호출됩니다. 레이어를 조작하여 레이어를 이동하고 속성 (예: opacity)을 변경하여 무려 60fps로 멋진 작업을 수행할 수 있습니다.

다음은 컴포지터 워크릿을 사용하는 시차 스크롤의 전체 구현입니다.

// 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];
    };
});

로버트 플랙은 개발자가 직접 사용해 볼 수 있도록 컴포지터 워크릿을 위한 폴리필을 작성했으며, 이는 분명히 성능에 훨씬 더 큰 영향을 미칩니다.

레이아웃 워크렛 (사양)

첫 번째 실제 사양 초안이 제안되었습니다. 구현까지는 시간이 꽤 걸립니다.

이 사양도 사실상 비어 있지만 개념은 흥미롭습니다. 나만의 레이아웃을 작성하세요. 레이아웃 워크렛을 사용하면 display: layout('myLayout')를 실행하고 JavaScript를 실행하여 노드의 자식을 노드의 상자에 정렬할 수 있습니다.

물론 CSS의 flex-box 레이아웃을 위한 전체 JavaScript 구현을 실행하는 것은 이에 상응하는 네이티브 구현을 실행하는 것보다 느리지만, 요령을 사용하면 성능을 향상할 수 있는 시나리오를 쉽게 상상할 수 있습니다. 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 (사양)

유형화된 CSSOM (CSS 객체 모델 또는 CSS 계층 구조 시트 객체 모델)은 모두가 한 번쯤은 겪어 보고 그냥 참는 법을 배운 문제를 해결합니다. JavaScript 한 줄로 설명해 보겠습니다.

    $('#someDiv').style.height = getRandomInt() + 'px';

수학을 실행하여 숫자를 문자열로 변환하고 단위를 추가하여 브라우저가 문자열을 파싱하고 CSS 엔진을 위해 다시 숫자로 변환하도록 합니다. JavaScript로 변환을 조작하면 더 엉망이 됩니다. 더 이상은 아닙니다. 이제 CSS를 입력하려고 합니다.

이 초안은 좀 더 완성도가 높은 초안 중 하나이며 폴리필이 이미 작업 중입니다. (면책 조항: 폴리필을 사용하면 계산 오버헤드가 더욱 늘어납니다. API가 얼마나 편리한지 보여주는 것이 목적입니다.)

문자열 대신 요소의 StylePropertyMap에서 작업하게 되며 여기서 각 CSS 속성에는 자체 키와 해당하는 값 유형이 있습니다. width와 같은 속성의 값 유형은 LengthValue입니다. LengthValueem, rem, px, percent와 같은 모든 CSS 단위의 사전입니다. 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}

속성 및 값

(사양)

CSS 맞춤 속성 (또는 비공식 별칭인 'CSS 변수')을 알고 계신가요? 유형이 있는 경우 지금까지는 변수에 문자열 값만 사용할 수 있었으며 간단한 검색 및 바꾸기 접근 방식을 사용했습니다. 이 초안을 사용하면 변수의 유형을 지정할 뿐만 아니라 기본값을 정의하고 JavaScript API를 사용하여 상속 동작에 영향을 줄 수도 있습니다. 기술적으로는 맞춤 속성을 표준 CSS 전환 및 애니메이션으로 애니메이션 처리할 수도 있으며, 이 역시 고려 중입니다.

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

글꼴 측정항목

글꼴 측정항목은 정확히 말 그대로입니다. 크기 Z의 글꼴 Y로 문자열 X를 렌더링할 때 경계 상자는 무엇인가요? 루비 주석을 사용하면 어떻게 되나요? 많은 요청이 있었으며 Houdini가 마침내 이 소원을 이루어줄 것입니다.

잠시만 기다려 주세요.

Houdini의 초안 목록에는 더 많은 사양이 있지만 향후 사용 여부는 불확실하며 아이디어의 자리표시자 이상은 아닙니다. 예를 들어 맞춤 오버플로 동작, CSS 문법 확장 API, 네이티브 스크롤 동작 확장 등이 있으며, 이러한 기능은 모두 이전에는 불가능했던 작업을 웹 플랫폼에서 지원합니다.

데모

데모용 코드(polyfill을 사용하는 실시간 데모)를 오픈소스로 제공했습니다.