Houdini - CSS 이해하기

CSS가 수행하는 작업의 양에 대해 생각해 본 적 있으신가요? 단일 속성을 변경한 후 갑자기 전체 웹사이트가 다른 레이아웃으로 표시됩니다. 일종의 마법같죠. 지금까지 웹 개발자 커뮤니티인 우리 모두는 그 마법을 목격하고 관찰할 수밖에 없었습니다. 우리가 자신만의 마법을 만들고 싶다면? 마술사가 되고 싶다면 어떻게 해야 할까요?

Houdini에 들어오세요!

Houdini 태스크포스는 Mozilla, Apple, Opera, Microsoft, HP, Intel, Google의 엔지니어로 구성되어 CSS 엔진의 특정 부분을 웹 개발자에게 노출합니다. 이 태스크포스는 W3C에서 실제 웹 표준이 될 수 있도록 초안 모음을 만들고 있습니다. 몇 가지 개괄적인 목표를 설정한 후 사양 초안으로 전환했고, 이를 통해 이를 뒷받침하는 하위 수준 사양 초안이 탄생했습니다.

이러한 초안 컬렉션은 일반적으로 누군가가 'Houdini'에 대해 이야기할 때 의미하는 것입니다. 이 문서 작성 시점에는 초안 목록이 완전하지 않으며 일부 초안은 단순한 자리표시자입니다.

사양

Worklet (spec)

Worklet 그 자체는 그다지 유용하지 않습니다. 많은 이후 초안을 가능하게 만들기 위해 도입된 개념입니다. 'worklet'을 읽을 때 Web Workers를 생각했다면 틀린 것이 아닙니다. 이들은 개념적으로 많은 부분이 겹칩니다. 그렇다면 이미 작업자가 있는데 왜 새로운 것일까요?

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

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

CSS Paint API (spec)

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

컴포지터 워크렛

여기에 설명된 API는 더 이상 사용되지 않습니다. 컴포지터 Worklet이 재설계되어 이제 'Animation Worklet'으로 제안됩니다. 자세한 내용은 현재 버전의 API를 참조하세요.

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

브라우저는 일반적으로 DOM 트리를 취하고 특정 기준에 따라 일부 브랜치와 하위 트리에 자체 레이어를 제공하기로 결정합니다. 이러한 하위 트리는 그 위에 자신을 페인트합니다 (향후 페인트 Worklet을 사용할 수 있음). 마지막 단계로, 이제 페인트된 모든 개별 레이어가 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];
    };
});

로버트 플랙이 컴포지터 워크릿을 위한 polyfill을 작성했으므로 직접 사용해 볼 수 있습니다. 확실히 성능 영향은 훨씬 높습니다.

레이아웃 Worklet (spec)

첫 번째 실제 사양 초안이 제안되었습니다. 조만간 구현하는 것이 좋습니다.

이번에도 사양은 사실상 비어 있지만 개념은 흥미롭습니다. 직접 레이아웃을 작성해야 합니다. 레이아웃 Worklet을 사용하면 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 (spec)

입력된 CSSOM (CSS 객체 모델 또는 Cascading Style Sheets 객체 모델)은 우리 모두가 경험했고 이제 막 참는 방법을 터득한 문제를 해결합니다. JavaScript 코드 한 줄로 설명하겠습니다.

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

여기서는 숫자를 문자열로 변환하여 단위를 추가하여 브라우저가 해당 문자열을 파싱하고 CSS 엔진에 맞게 다시 숫자로 변환하도록 합니다. 이는 JavaScript로 변환을 조작할 때 더 악화됩니다. 더 이상은 아닙니다. 이제 CSS를 입력하려고 합니다.

이 초안은 좀 더 완성도가 높은 초안 중 하나이며 polyfill이 이미 작업 중입니다. (면책 조항: 폴리필을 사용하면 더 많은 계산 오버헤드가 분명히 추가됩니다. 요점은 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}

속성 및 값

(spec)

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을 사용하는 실시간 데모)를 오픈소스로 제공했습니다.