Анимации в вебе

Никита Дубко, Яндекс

Анимации в вебе

Никита Дубко, Яндекс

Кто я?

Анима́ция

от фр. animation «оживление; одушевление»

Зачем нужны анимации?

awwwards.com/websites/animation/

<marquee>

Всё потому что я разведчик, и меня почти не видно! DVD

Библиотеки CSS-анимаций

CSS Animation Libraries
Полное руководство по правильному использованию анимации в UX

Как делать анимации производительными?

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

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

Опасные свойства ❌

csstriggers.com

Нейтральные свойства ⚠️

Безопасные свойства ✅

GPU ⚡️

CSS GPU Animation: Doing It Right

Это не часть
спецификации!
🙅‍♂️

/* Intrinsic reasons that can be known right away by the layer. */
V(3DTransform)                                                              \
V(Video)                                                                    \
V(Canvas)                                                                   \
V(Plugin)                                                                   \
V(IFrame)                                                                   \
V(BackfaceVisibilityHidden)                                                 \
V(ActiveTransformAnimation)                                                 \
V(ActiveOpacityAnimation)                                                   \
V(ActiveFilterAnimation)                                                    \
V(ActiveBackdropFilterAnimation)                                            \
V(ScrollDependentPosition)                                                  \
V(OverflowScrollingTouch)                                                   \
V(OverflowScrollingParent)                                                  \
V(OutOfFlowClipping)                                                        \
V(VideoOverlay)                                                             \
V(WillChangeTransform)                                                      \
chromium//src/third_party/blink/renderer/platform/graphics/compositing_reasons.h

Плюсы выноса на GPU 🙂

Минусы выноса на GPU 🙁

V(3DTransform)
...
V(OverflowScrollingTouch)
...
V(WillChangeTransform)
chromium//src/third_party/blink/renderer/platform/graphics/compositing_reasons.h
iPhone Чегооо?

Containing block

developer.mozilla.org/en-US/docs/Web/CSS/Containing_block

Очень странное решение

.gallery__item {
    animation-name: scroller-overflow-hack;
    animation-fill-mode: forwards;
    animation-duration: 1ms;
}

@keyframes scroller-overflow-hack {
    0% { opacity: .9999; }
    100% { opacity: 1; }
}
Get started with GPU Compute on the Web

👨🏻‍🔬 contain:
layout  size  paint

developer.mozilla.org/en-US/docs/Web/CSS/contain
Helping Browsers Optimize With The CSS Contain Property

Как исследовать? 🔍

Инкогнито-режим 🦉

CPU Throttle 🐌

Rendering

Paint flashing 💡

Animations

Layers

Performance

Performance Monitor

Чем анимировать?

animation

easings.net

Особенности CSS-анимаций

animation или transition?

transition

transition: all ease 0.2s;

transition: color ease 0.2s;

🔥

demo
@media screen and
(prefers-reduced-motion: reduce),
(update: slow) {
    * {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
    }
}
Designing Safer Web Animation For Motion Sensitivity

Web Animations API

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

Polyfill

Using the Web Animations API

Web Animations API

document.getAnimations().forEach(
    function (animation) {
        animation.playbackRate *= 0.5;
    }
);

CSS Animation Worklet API
🔮

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

        animate(currentTime, effect) {
            effect.localTime = currentTime * this.coef;
        }
    });
Houdini's Animation Worklet, Surma
// app.js
await CSS.animationWorklet.addModule("animator.js");

new WorkletAnimation(
    'scroll-position-animator',
    keyframeEffect,
    scrollTimeline,
    { coef: 1.2 }
).play();
const keyframeEffect = new KeyframeEffect(
    document.querySelector('#target'),
    [
        { transform: 'translateX(0)' },
        { transform: 'translateX(500px)' }
    ],
    {
        duration: 2000,
        iterations: Number.POSITIVE_INFINITY
    }
);
const scrollTimeline = new ScrollTimeline({
    timeRange: 2000,
    scrollSource: document.querySelector('.source'),
    orientation: 'vertical',
    startScrollOffset: '200px',
    endScrollOffset: '500px'
});

Полифил anim-worklet

Демо scroller

Is Houdini ready yet‽

SVG-анимации

<animateMotion
    xlink:href="#circle"
    dur="1s"
    begin="click"
    fill="freeze"
    path="M0,0c3.2-3.4,18.4-0.6,23.4-0.6c5.7,0.1,10.8,0.9,16.3,2.3    c13.5,3.5,26.1,9.6,38.5,16.2c12.3,6.5,21.3,16.8,31.9,25.4c10.8,8.7,21,18.3,31.7,26.9c9.3,7.4,20.9,11.5,31.4,16.7
    c13.7,6.8,26.8,9.7,41.8,9c21.4-1,40.8-3.7,61.3-10.4c10.9-3.5,18.9-11.3,28.5-17.8c5.4-3.7,10.4-6.7,14.8-11.5
    c1.9-2.1,3.7-5.5,6.5-6.5" />
A Guide to SVG Animations (SMIL)

stroke-dashoffset

Как анимировать SVG подпись

Canvas

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

function draw(time) {
    const w = canvas.width;
    const h = canvas.height;
    ctx.fillStyle = '#111';
    ctx.fillRect(0, 0, w, h);

    requestAnimationFrame(draw);
}
requestAnimationFrame(draw);

requestAnimationFrame

requestAnimationFrame Scheduling For Nerds
Super Mario Bros in JavaScript

CSS Painting API 🎨

Демо image-placeholder

CSS Painting API

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

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) {
        // Можно рисовать почти как на обычном canvas
    }
});
/* style.css */
.my-element {
    --foo: deeppink;
    background-image: paint(my-paint);
}

// app.js
CSS.paintWorklet.addModule('my-paint.js');

Ограничения Paint Worklet

Chrome 65+

css-paint-polyfill

WebGL 👀

Юрий Артюх — All Your HTML

youtube.com/channel/UCDo7RTzizoOdPjY8A-xDR7g

Earth World (by akella)

Ускоряем WebGL/Three.js с помощью OffscreenCanvas и веб-воркеров
How to Animate on the Web With GreenSock

Альтернативные способы

GIF

GIF

Делайте из слона муху / Вадим Макеев

Быстрая смена JPG 🙈

Видео 😁

WebM 🎭

Как ускориться? 🚀

CSS Typed OM

CSS Typed OM Level 1
const element = document.querySelector('.elem');

const styleMap = element.attributeStyleMap;
console.log( styleMap.get('font-size') );
// CSSUnitValue {value: 2.5, unit: "em"}
const computedStyleMap = element.computedStyleMap();
console.log( computedStyleMap.get('font-size') );
// CSSUnitValue {value: 25, unit: "px"}
const styleMap = element.attributeStyleMap;
const transformValue = new CSSTransformValue([
    new CSSTranslate(
        CSS.px(50),
        new CSSMathSum(CSS.em(1), CSS.px(5))
    )
]);
styleMap.set('transform', transformValue);

Проверить поддержку

if (window.CSS && CSS.number) {
    // В вашем браузере CSS Typed OM работает
}
        

Chrome 66+

Layout + Reflow 🐌

What forces layout / reflow

Ловушка 😱

function frame() {
    const element = document.querySelector('.block');
    element.style.width = element.offsetWidth * 2;
};
requestAnimationFrame(frame);

Большие картинки ⬛️
в маленьких контейнерах ▫️

will-change

Everything You Need to Know About the CSS will-change Property
.child {
    transition: opacity .3s linear;
}

.parent:hover .child {
    will-change: opacity;
}

.child:hover {
    opacity: .5;
}
var el = document.getElementById('element');

el.addEventListener('mouseenter', hintBrowser);
el.addEventListener('animationEnd', removeHint);

function hintBrowser() {
    this.style.willChange = 'transform, opacity';
}

function removeHint() {
    this.style.willChange = 'auto';
}

{ passive: true }

document.querySelector('.block')
    .addEventListener(
        'scroll',
        () => {...},
        { passive: true }
    );
Improving scrolling performance with passive listeners

Своевременная
анимация ⏱

Задержка выполнения

Проверяйте на реальных устройствах ☎️

Материалы

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

mefody.github.io/talks/web-animations/
@dark_mefody
mefody@yandex-team.ru
Parser DOM / CSSOM Cascade Layout Paint Composite