Houdini — великий разоблачитель

Никита Дубко, Capital Decision.

Houdini — великий разоблачитель


Никита Дубко, Capital Decision

Hands on the orange typewriter in a park
pixabay.com

Конвейер рендеринга в браузере

Конвейер рендеринга в браузере

  1. Скачивание и парсинг HTML, CSS и JavaScript

Конвейер рендеринга в браузере

  1. Скачивание и парсинг HTML, CSS и JavaScript
  2. Построение Document Object Model и CSS Object Model

Конвейер рендеринга в браузере

  1. Скачивание и парсинг HTML, CSS и JavaScript
  2. Построение Document Object Model и CSS Object Model
  3. Формирование дерева рендеринга

Конвейер рендеринга в браузере

  1. Скачивание и парсинг HTML, CSS и JavaScript
  2. Построение Document Object Model и CSS Object Model
  3. Формирование дерева рендеринга
  4. Расчет положения на странице каждого элемента дерева рендеринга

Конвейер рендеринга в браузере

  1. Скачивание и парсинг HTML, CSS и JavaScript
  2. Построение Document Object Model и CSS Object Model
  3. Формирование дерева рендеринга
  4. Расчет положения на странице каждого элемента дерева рендеринга
  5. Отрисовка пикселей каждого слоя

Конвейер рендеринга в браузере

  1. Скачивание и парсинг HTML, CSS и JavaScript
  2. Построение Document Object Model и CSS Object Model
  3. Формирование дерева рендеринга
  4. Расчет положения на странице каждого элемента дерева рендеринга
  5. Отрисовка пикселей каждого слоя
  6. Компоновка слоев и отображение в видимой области браузера

Конвейер рендеринга в браузере

  1. Магия
  2. Можем влиять на процесс из JavaScript
  3. Магия
  4. Магия
  5. Магия
  6. Магия

The Extensible Web Manifesto

The Extensible Web Manifesto 10.06.2013

Сосредоточиться на добавлении новых безопасных и эффективных низкоуровневых возможностей веб-платформы.

Раскрывать низкоуровневые возможности, что объяснит существующие функции, такие как HTML и CSS, позволяя авторам понимать и воспроизводить их.

Упростить и оптимизировать долгосрочный процесс стандартизации новых API, которые будут иметь применение и значительное использование в реальных условиях.

JavaScript

Полифил — это код, реализующий какую-либо функциональность, которая не поддерживается в некоторых версиях веб-браузеров по умолчанию.

Полифил

(function(self) {
	'use strict';
	if (self.es2020feature) {
		return;
	}
	self.es2020feature = function() {
		// Do some magic
	};
	self.es2020feature.polyfill = true;
})(this);

Понифил 🦄

ponyfill.com

// is-nan-ponyfill.js
module.exports = function (value) {
	return value !== value;
};

// app.js
var isNanPonyfill = require('is-nan-ponyfill');
isNanPonyfill(5);

Транспайлер

// ES6
let point = { x: 0, y: 0 };
const {x, y} = point;
const arrowFunction = (a) => a ** 2;

// ES5
"use strict";
var point = { x: 0, y: 0 };
var x = point.x,
    y = point.y;
var arrowFunction = function arrowFunction(a) {
  return Math.pow(a, 2);
};
Логотип 6to5
Логотип Babel
Polymer logo

CSS

Ошибки в проектировании CSS

LESS SASS Stylus PostCSS

Polyfill.js 🎉

Polyfill({
		declarations:["position:sticky"]
	})
	.doMatched(function(rules) {
		/* add styles */
	 })
	.undoUnmatched(function(rules) {
		/* reset styles */
	});

Василий Ванчук — Отполифиль свой CSS на MinskCSS Meetup #2

Polyfill.js author's message

CSS-TAG Houdini Task Force 🎉

CSS-TAG Houdini Task Force
Гарри Гудини
Новые спецификации Houdini
Новые спецификации Houdini для работы с конвейером рендеринга
CSS Houdini specifications

Репозиторий проекта на Github

CSS Custom Properties

/* CSS */
:root {
	--r: 255;
	--g: 20;
	--b: 147;
	--deeppink-color: rgb(var(--r), var(--g), var(--b));
}

.pink-element {
	--element-width: 200px;
	--element-border-width: 20px;
	background-color: var(--deeppink-color);
	width: calc(var(--element-width) + var(--element-border-width));
}
// JavaScript
const isSupported = CSS.supports('--custom', 'property');

// Получить значение custom property
const pinkElement = document.querySelector('.pink-element');
const elementWidth = window
   .getComputedStyle(pinkElement)
   .getPropertyValue('--element-width');

// Изменить custom property для узла
pinkElement.style.setProperty('--element-width', '300px');

// Изменить custom property в :root
document.documentElement.style.setProperty('--r', 190);
Custom Properties — caniuse.com

See the Pen #DailyCssImages - Day 17. Zombie by Nikita Dubko ( @dark_mefody) on CodePen.

CSS Parser API

CSS Parser API

var background = window.cssParse.rule("background: green");
console.log(background.styleMap.get("background").value) // "green"

var styles = window.cssParse
	.ruleSet(".foo { background: green; margin: 5px; }");
console.log(styles.length) // 5
console.log(styles[0].styleMap.get("margin-top").value) // 5
console.log(styles[0].styleMap.get("margin-top").type) // "px"

const style = fetch("style.css")
	.then(response => CSS.parseStylesheet(response.body));
style.then(console.log);

CSS Typed OM

Проблема

const example = document.querySelector('.slide');
const elementFontSize = parseFloat(
	window.getComputedStyle(example).getPropertyValue('font-size')
);
console.log(elementFontSize); // 25

example.style.setProperty(
	'transform',
	'translate(50px, calc(1em + 5px))'
);

Решение

const exampleStyleMap = document.querySelector('.slide').styleMap;
const fontSizeValue = exampleStyleMap.get('font-size').value;
console.log( exampleStyleMap.get('font-size') );
// CSSUnitValue {value: 25, unit: "px", type: "length"}

const calcValue = new CSSMathSum(CSS.em(1), CSS.px(5));
const transformValue = new CSSTransformValue([
	new CSSTranslation(
		new CSSUnitValue(50, 'px'),
		calcValue
	)]);
exampleStyleMap.set('transform', transformValue);

Иерархия возможных значений свойств

typed-om

chrome://flags

Experimental flag

CSS Properties and Values API

// JavaScript
CSS.registerProperty({
	name: '--sidebar-width',
	syntax: '<length>',
	inherits: true,
	initialValue: '80px'
});
/* CSS */
@property --sidebar-width {
	syntax: "<length>",
	inherits: true,
	initialValue: "80px"
}

Синтаксис

<length>
<number>
<percentage>
<length-percentage>
<color>
<image>
<url>
<integer>
<angle>
<time>
<resolution>
<transform-function>

<length | number>
<length+>
small | smaller

Демо sidebar

Worklets

// app.js — псевдокод
window.someWorklet
	.addModule('some-worklet.js')
	.then(_ => {
		console.log('some-worklet — loaded');
	});

// some-worklet.js — псевдокод
registerSomeWorklet('some-worklet-name', class {
	process(arg) {
		// делаем магию
	}
});

CSS Painting API 🎨

CSS Painting API

Можно применять для отрисовки свойств:

// my-paint.js
registerPaint('my-paint', class MyPaint {
	static get inputProperties() { return ['--foo']; }
	static get inputArguments() { return ['<color>']; }
	static get contextOptions() { return { alpha: true }; }

	paint(ctx, geom, properties, args) {
		// ctx - контекст для рисования, как в canvas
		// geom - размеры доступной для рисования области
		// properties - свойства, на которые реагирует paintWorklet
		// args - переданные в paintWorklet аргументы
		// Можно рисовать почти как на обычном canvas
	}
});
/* style.css */
.my-element {
	--foo: deeppink;
	background-image: paint(my-paint);
}
// app.js
CSS.registerProperty({
	name: '--foo',
	syntax: '<color>',
	inherits: true,
	initialValue: 'black',
});
CSS.paintWorklet.addModule('my-paint.js');

Демо ripple

Демо border-colors

Демо border-slicer

Демо image-placeholder

Box Tree API



<style>
p::first-line { color: green; }
p::first-letter { color: red; }
</style>

<p>foo <i>bar baz</i></p>

foo bar baz

CSS Layout API 🚀

registerLayout('my-layout', class myLayout {
	static get inputProperties() { return ['--foo']; }

	static get childrenInputProperties() { return ['--bar']; }

	static get childDisplay() { return 'normal'; } // normal | block

	*intrinsicSizes(children, styleMap) {
		// Вычислить внутрение размеры элемента
	}

	*layout(space, children, styleMap, edges, breakToken) {
		// Разместить всех children внутри space, учитывая edges
	}
});
/* CSS */
.layout-example {
	--foo: 5;
	display: layout(my-layout);
}

.layout-example .child {
	--bar: 200px;
}
// JavaScript
window.layoutWorklet.addModule('my-layout.js');

Демо layout(hexagon)

CSS Animation Worklet API

Web Animations

// JavaScript
const element = document.querySelector('.my-element');
element.animate([
	{'--some-color': 'red',  'opacity': 0 },
	{'--some-color': 'blue', 'opacity': 1},
], {
	direction: 'alternate',
	duration: 5000,
	iterations: 10, // Infinity
});

Polyfill

// worklet.js
registerAnimator(
	'scroll-position-animator',
	class {
		constructor(options) {
			this.options = options;
		}

		animate(currentTime, effect) {
			effect.children.forEach((child) => {
				child.localTime = currentTime;
			});
		}
	});
// app.js

const frames = [
	new KeyframeEffect(
		document.querySelector('.scroll-to-top'),
		[
			{ 'transform': 'translateY(100px)', 'opacity': '0' },
			{ 'transform': 'translateY(0)', 'opacity': '1' }
		],
		{ duration: 1, iterations: 1, fill: 'both' }
	)
];
// app.js

const timeline = new ScrollTimeline({
	scrollSource: document.querySelector('.page-wrapper'),
	orientation: 'vertical',
	startScrollOffset: '200px',
	endScrollOffset: '300px',
});
// app.js

animationWorklet.addModule('worklet.js').then(() => {
	const animation = new WorkletAnimation(
		'scroll-position-animator',
		frames,
		timeline
	);
	animation.play();
});

anim-worklet

Демо scroll-animation

Демо от Google

Friends

Font Metrics API

Метрики шрифтов
Барни Стинсон

Почему Houdini хорош?

Недостатки Houdini

http://ishoudinireadyyet.com

Is Houdini ready yet‽

Материалы по теме

Статьи

  1. Houdini: Maybe The Most Exciting Development In CSS You’ve Never Heard Of, Филип Уолтон
  2. Houdini: Demystifying CSS, Алекс Сурма

Видео

  1. Houdini: Demystifying the Future of CSS — Google I/O 2016
  2. Philip Walton | Polyfills & Houdini | Browser API Special — CSS Day 2017
  3. Serg Hospodarets: CSS Houdini - from CSS variables to JavaScript and back — Frontend United 2017
  4. Magic Tricks with CSS Houdini (Sam Richard) — Full Stack Fest 2017

Спасибо за внимание!

mefody.github.io/talks/houdini-magic/
@dark_mefody
n.a.dubko@gmail.com

QR code — ссылка на презентацию
Parser DOM / CSSOM Cascade Layout Paint Composite