class: middle, center, naked name: intro
# Co-building and maintaining
animations over time Thibault Mahé - may 2017 - v1 --- class: c-slide-list-lg # Bringing UI to life .u-large[Why animating an interface can enhance the UX?]
- Convey **meaning**: "Motion is meaningful" (Material Design) - Focus **attention** - Maintain **continuity** - Reduce **cognitive load** - Describe **components relationships** - Explain the intent of a **functionality** - Is part of the **Identity**: character, beauty and delight --- class: c-slide-list-lg # Designing over time .u-large[Animating is...] - ...bringing an element from a state to another - ...bringing the concept of **time** to a UI
.c-blockquote.u-center[An animation is
**a .u-underline[set of values]
.u-underline[over time]**] --- # Designing over time
See the Pen
Single element Slack loader
by CrocoDillon (
@CrocoDillon
) on
CodePen
.
.u-center[Loader] --- # Designing over time
.u-center[Parallax] --- # EX: how CSS manages values over time .u-pd-bottom[ - Set of values: `transform` property values - Time: `animation-duration`, `animation-delay`, `animation-iteration-count`, ... - Repartition over time: [0%, ..., 50%, ..., 100%] - Easing: `animation-timing-function` ]
See the Pen
Keyframes animation sample
by tibomahe (
@tibomahe
) on
CodePen
.
--- # EX: how SMIL manages values over time .u-pd-bottom[ - Set of values: `values`, `from`, `to`, ... - Time: `dur`, `repeatCount`, `begin`, `end`, ... - Repartition over time: `keytimes`, ... - Easing: `calcMode`, `keySplines`, ... ]
See the Pen
KmRrRo
by tibomahe (
@tibomahe
) on
CodePen
.
--- class: c-slide-list-lg # Different methods of animation - CSS - SVG (SMIL) - JavaScript - Vanilla - Web Animation API (new) - jQuery - canvas, Webgl - Libraries: D3, Greensock, ... - ... ??? - Toutes permettent de manipuler des données (des positions, des facteurs de couleurs, etc.) sur du temps --- class: c-slide-list-lg # Different methods of animation - CSS is great for managing styles... - made for it - performance (CSSOM) - native animation (keyframes) - ... but JavaScript allows more configuration - events - variables - dynamic values - loop, condition, ... ??? - M'appesantir sur une (pas si) nouvelle techno --- class: naked, middle, center name: observables # Observables --- # The (not so) new hype tech
.l-flex-row[ .l-flex-col.u-pd[ .c-img--sm.u-center[![angular 2](im/angular2.jpg)] .u-center[*Angular 2*] ] .l-flex-col.u-pd[ .c-img--sm.u-center[![cycle JS](im/cyclejs.svg)] .u-center[*Cycle JS*] ] ] ??? - Pourquoi ca peut nous intéresser --- # The (not so) new hype tech .c-img--lg.u-center[![ material motion](im/material-motion.png)] .u-center[*Material Motion*] ??? - Pourquoi ca peut nous intéresser - Material motion a (notamment) 3 ambitions: - construire un langage de concept autour des animations - que ces concepts correspondent à des "composants" concrets et réutilisables - que ca permettent de faire le pont entre designers et ingénieurs - On reviendra plus tard sur ces ambitions --- class: c-slide-list-lg # Overview - What is an Observable - How to use it - Concrete sample - Advantages - Concept of "Reactive animation" --- class: c-slide-list-lg # What is an Observable? .l-flex-row[ .l-flex-col--33.u-pd[ - **Value**: ] .l-flex-col.u-pd[
] ] ??? - Value = valeur seule, comme un nombre, un boolean, un objet, ... -- .l-flex-row[ .l-flex-col--33.u-pd[ - **Array**: ] .l-flex-col.u-pd[
[
]
] ] ??? - Array ou iterable = tableau fini de valeur -- .l-flex-row[ .l-flex-col--33.u-pd[ - **Promise**: ] .l-flex-col.u-pd[
.
.
.
] ] ??? - Intervention du temps ! - Promise = une valeur qui va arriver après un certain temps -- .l-flex-row[ .l-flex-col--33.u-pd[ - **Observable**: ] .l-flex-col.u-pd[
-
-
-
-
>
] ] ??? - Observable (ou stream) = un nombre fini ou indéfini de valeurs qui arrivent dans le temps - Un observable est donc assez similaire à un Array: une collection de valeurs, qui peut être manipuler par des fonctions comme map, filter, reduce, ... - MAIS un Observable intègre la notion de temps: il y a un temps et une fin, et les valeurs arrivent à différents moments --
.c-blockquote.u-center[An Observable is **a .u-underline[set of values] .u-underline[over time]**] ??? - Exactement la même définition que les animations - Ce qui signifie que l'on pourrait traduire des animations en observables --- class: c-slide-list-lg # What is an Observable? .u-large[Observables are:] - **Asynchronous**: data comes at any **time** - **Immutable**: transforming an observable **doesn't modify** it but creates instead a new one - **Subscribable**: an observable don't store data, an Observable is a **bridge**, connecting an event source to a listener (**Observer**) so we can know when things change. --- class: c-slide-list-lg # What is an Observable? .u-large[An observable is an "interface" that can:] .l-flex-row[ .l-flex-col--33[ - Catch some data ] .l-flex-col[ ] ] .l-flex-row[ .l-flex-col--33[ - Transform these data ] .l-flex-col[ ] ] .l-flex-row[ .l-flex-col--33[ - Let these data be read ] .l-flex-col[ ] ] --- class: c-slide-list-lg # What is an Observable?
.l-flex-row[ .l-flex-col--33[ - Catch some data ] .l-flex-col[ .u-large[1- Create an **Observable**] ] ] .l-flex-row[ .l-flex-col--33[ - Transform these data ] .l-flex-col[ .u-large[2- Manipulate a stream: the **operators**] ] ] .l-flex-row[ .l-flex-col--33[ - Let these data be read ] .l-flex-col[ .u-large[3- Listen to a stream: the **subscription**] ] ] ??? - 3 concepts à voir --- class: c-slide-list-lg # 1. Create an **Observable** .u-large[Streams can be created from **anything**:] - Variables, - DOM Events, - Mouse / Keyboard interactions, - Animations, - Caches, - Data structures, - Anything.
.u-large[ We can **listen** to those streams and **react** accordingly] ??? - je précise bien "anything" car ca aura son importance plus tard --- # 1. Create an **Observable**
```javascript var myvariable = 'foo'; // The observable emits that value, then completes. var variableStream$ = Rx.Observable.of(myvariable); // The observable emits each item from the array, then completes. var arrayStream$ = Rx.Observable.from([1, 2, 3, 4, 5]); // The observable emits the promise result eventually, then completes (or errors). var promiseStream$ = Rx.Observable.fromPromise(fetch('http://example.com/users')); // The observable continuously emits events from the event listener. var mouseMove$ = Rx.Observable.fromEvent(window, 'mousemove'); ```
.u-center[*Using Rx.js, a JavaScript implementation of ReactiveX*] --- # RX Marbles .l-flex-row[ .l-flex-col.u-pd[ ![rx marbles](im/rxmarbles.png) ] .l-flex-col--33.u-pd[ ![https://gist.github.com/staltz/868e7e9bc2a7b8c1f754](im/click-on-button-stream.png) .u-center[--a---b---c---d---X---|->] ] ] .l-flex-row[ .l-flex-col.u-pd[ .u-center[*Visualizing Observables*] ] .l-flex-col--33.u-pd[ .u-center[*Shematisation*] ] ] ??? - Visualiser les streams avec des billes - On voit par ailleurs ici la dimension asynchrone de l'Observable ainsi que sa dimension immuable --- class: c-slide-list-lg # 2. Manipulate a stream: the **operators** .l-flex-row[ .l-flex-col.u-pd[ ![map](im/map.png) ] .l-flex-col.u-pd[ ![filter](im/filter.png) ] ] - **Transforming**: delay, map, scan (reduce), debounce, ... - **Combining**: concat, merge, withLatestFrom, zip, ... - **Filtering**: filter, first, last, takeWhile, average, ... - **Mathematical operators**: count, max, min, sum, ... - and many many more... ??? - Un Observable seul ne sert pas à grand chose. Des opérateurs vont permettre de manipuler chaque stream pour en créer des nouveaux. - Ces manipulations sont très proches de celles que propose l'API des tableaux JavaScript (map, reduce, filter, ...). - 1 ou plusieurs stream(s) peut être utilisé comme un input pour un autre stream --- # 2. Manipulate a stream: the **operators** - Example 1: observe a click on a button and use the **scan operator** to get the number of time the button is clicked.
From --{e}--{e}--{e}--{e}--{e}--> to --0--1--2--3--4-->
See the Pen
Rx click stream sum demo
by tibomahe (
@tibomahe
) on
CodePen
.
??? - Je vous donne un exemple concret pour ne pas que vous saignez du nez trop tôt - Je transform le stream d'event en un nombre en partant de 0, et j'incrémente de 1 à chaque valeur --- # 2. Manipulate a stream: the **operators** - Example 2: observe the mouse position and use the **map operator** to transform the X coordinate into a 0 to 360 value for the hue parameter of a gradient on the UI.
From --{e}--{e}--{e}--{e}--{e}--> to --126--8--267--98--350-->
See the Pen
RxJS - Sample mousemove and map
by tibomahe (
@tibomahe
) on
CodePen
.
??? - Je récupère les valeurs X et Y (pos) - Je divise le X par la taille de l'écran pour avoir une valeur entre 0 et 1, et je mulitplie par 360 pour avoir les valeurs nécessaires de teinte --- # 3. Subscribing to an observable .l-flex-row[ .l-flex-col[ ![https://gist.github.com/staltz/868e7e9bc2a7b8c1f754](im/click-on-button-stream.png) ] .l-flex-col.u-pd--md[ - A stream can **emit** 3 things: + a value + an error + a "completed" signal ] ] ```javascript stream$.subscribe( (value) => doSomething(value), (error) => console.error(error), () => console.log('complete') ); ``` --- class: c-slide-list-lg # Animation from Observables .u-large[Example of a basic non-reactive animation: a moving ball]
.l-flex-row[ .l-flex-col--33.u-pd[ - a frame rate ] ] .l-flex-row[ .l-flex-col--33.u-pd[ - a duration ] ] .l-flex-row[ .l-flex-col--33.u-pd[ - a velocity ] ] .l-flex-row[ .l-flex-col--33.u-pd[ - an easing? ] ] ??? - Comment définir une animation? - commencer par une animation simple --- class: c-slide-list-lg # Animation from Observables .u-large[Example of a basic non-reactive animation: a moving ball]
.l-flex-row[ .l-flex-col--33.u-pd[ - a frame rate ] .l-flex-col.u-pd[ **Schedule** a stream on **animation frame** ] ] .l-flex-row[ .l-flex-col--33.u-pd[ - a duration ] .l-flex-col.u-pd[ Limit a stream with **takeWhile** ] ] .l-flex-row[ .l-flex-col--33.u-pd[ - a velocity ] .l-flex-col.u-pd[ **Map** a velocity function (px / ms) ] ] .l-flex-row[ .l-flex-col--33.u-pd[ - an easing? ] .l-flex-col.u-pd[ **Map** an easing function ] ] --- class: c-slide-list-lg # Animation from Observables .u-large[`Rx.Observable` ...]
.l-flex-row[ .l-flex-col--33.u-pd[ - a frame rate ] .l-flex-col.u-pd[ `.interval( 0, Rx.Scheduler.animationFrame )`] ] .l-flex-row[ .l-flex-col--33.u-pd[ - a duration ] .l-flex-col.u-pd[ `.takeWhile( (t) => t <= duration )`] ] .l-flex-row[ .l-flex-col--33.u-pd[ - a velocity ] .l-flex-col.u-pd[ `.map( (t) => velocity(t) )`] ] .l-flex-row[ .l-flex-col--33.u-pd[ - an easing? ] .l-flex-col.u-pd[ `.map( (t) => elasticOut(t) )`] ] ??? - Grossièrement c'est ce qui va se passer - Petits détails d'adaptation plus tard --- class: c-slide-list-lg # Animation from Observables .u-large[--0--1--2--3--4--5--6--7--8--9--10--[...]->]
.l-flex-row[ .l-flex-col--33.u-pd[ - a frame rate ] .l-flex-col.u-pd[ Stream of **intervals**: --0--1--2--3--4--5--6--7--8--9--10--[...]-> ] ] .l-flex-row[ .l-flex-col--33.u-pd[ - a duration ] .l-flex-col.u-pd[ Stream of **ms**: --2--6--17--18--31--32--48--[...]--1998--2012--|-> ] ] .l-flex-row[ .l-flex-col--33.u-pd[ - a velocity ] .l-flex-col.u-pd[ Stream of **px**: --2--4--8--[...]--123--125--[...]--297--300--|--> ] ] .l-flex-row[ .l-flex-col--33.u-pd[ - an easing? ] .l-flex-col.u-pd[ Stream of **px**: --1--2--4--[...]--61--65--[...]--297--300--|--> ] ] --- # Animation from Observables
See the Pen
RxJS Simple animation - sample 3 (repeat)
by tibomahe (
@tibomahe
) on
CodePen
.
??? - Defer: pour calculer la durée on récupère l'heure actuelle et on fait des différences à chaque intervalle. Defer permet de récupérer l'heure au moment de la subscription plutôt qu'au moment où le JS est parsé. - Duration: pour arrêter le stream, il a d'abord fallu transformer ses valeurs en intervalles de temps. - Easing: la fonction attend es valeurs entre 0 et 1 donc il a fallu adapter ces valeurs --- # Animation from Observables - Another example: loader
See the Pen
RxJS - Simple animation - Solar Eclipse sample
by tibomahe (
@tibomahe
) on
CodePen
.
--- class: c-slide-list-lg # Animation from Observables
- Listen to the animation frames given by the browser - Create a set of time differences on animation frame - Map those time differences into position differences - Subscribe and manipulate the DOM ??? - Exemple qui nous permet de comprendre un peu mieux les Observables - Mais les Observables ne sont pas forcément le meilleur outils pour gérer ce type d'animation. - Les CSS keyframes sont natives et très pratiques pour ce genre d'animation - Greensock est très performant et sa timeline est très claire - Canvas permet de faire du raster plutôt que des px et est très performant pour les animations complexes - Etc. - Pourquoi Material motion alors? - Les animations avec des Observables peuvent être à mon sens essentielles pour 2 raisons : leur réactivité et leur modularité --- # 1. Reactivity - Animation on input change
See the Pen
RxJS - Sample for input change
by tibomahe (
@tibomahe
) on
CodePen
.
--- # 1. Reactivity
See the Pen
RxJs | Body Mass Index Calculator
by Simonpietro Nonnis (
@SimonNonnis
) on
CodePen
.
--- # 1. Reactivity - Animation on mousemove
See the Pen
RxJS - Sample moving eye with CSS var
by tibomahe (
@tibomahe
) on
CodePen
.
--- # 1. Reactivity
See the Pen
Reactive O
by Varun Vachhar (
@winkerVSbecks
) on
CodePen
.
--- # 1. Reactivity - Animation on scroll
See the Pen
RxJS - Parallax sample animation POC
by tibomahe (
@tibomahe
) on
CodePen
.
??? - Effet de parallax et de fading --- class: c-slide-list-lg # 1. Reactivity - **Discrete changes** caused by any number of **events** - Already widely used in mobile environment
.l-flex-row[ .l-flex-col--50.u-pd[ .c-img--md.u-center[![reactive mobile sample](im/reactive-mobile-1.gif)] ] .l-flex-col--50.u-pd[ .c-img--md.u-center[![reactive mobile sample](im/reactive-mobile-2.gif)] ] ] ??? - Definition de la réactivité - Le premier intérêt des Observables pour les animations est qu'ils permettent d'"observer" les actions des utilisateurs et de réagir en fonction. - Une animation réactive utilise des valeurs dynamiques directement émises par les actions des utilisateurs. --- class: c-slide-list-lg # 2. Modularity - Interface **any event source** to any listener - Make uniform the way we deal with fetched data, WebSockets communication, external events from multiple sources, or even animations, ... .l-flex-row[ .l-flex-col--50.u-pd[ ```javascript // Event document.getElementById('foo') .addEventListener('click', listener); // Promise fetch('/foo/1') .then(listener) // Array [1, 2, 3, 4] .forEach(listener) ``` ] .l-flex-col--50.u-pd[ ```javascript clickStream$.subscribe({ next: listener }); fetchResponseStream$.subscribe({ next: listener }); arrayStream$.subscribe({ next: listener }); ``` ] ] ??? - Chaque méthode met son listener à différentes places - L'Observable est un wrapper qui leur donne à tous la même interface --- class: c-slide-list-lg # 2. Modularity - Promote "**Separation of concerns**" - We declaratively represent the data we expect using Observables and operators, and then deal with side effects in a single `.subscribe()`. .l-flex-row.u-bottom-align[ .l-flex-col--50.u-pd[ ```javascript var mouseStream$ = Rx.Observable .fromEvent(document, 'mousemove'); var manipulateStream$ = moveStream$ .map(function(pos) { return changeData(pos) }); ``` .u-center[*Manage data*] ] .l-flex-col--50.u-pd[ ```javascript manipulateStream$.subscribe(function(value) { updateUI(value); }); ``` .u-center[*Manage rendering*] ] ] --- class: c-slide-list-lg # 2. Modularity - It's **not a framework** - It can be used in many frameworks (React, Angular, and Vue) - It can be used in many languages (Java, PHP, Python, Ruby, C#, Swift, ...) - React, Elm (~Signals) --- class: c-slide-list-lg # 2. Modularity - Foster **reusability** - **Declarative** and **Functional** approach: abstraction - Observables can have multiple subscription, meaning they can be reused
See the Pen
RxJS - Sample mousemove and map
by tibomahe (
@tibomahe
) on
CodePen
.
??? - Dans cet exemple donné plus tôt, l'Observable `hueStream$` est utilisé à 2 reprises : pour changer le bkg et pour changer le label central --- class: c-slide-list-lg # 2. Modularity - Examples of Material Motion: .l-flex-row.u-top-align[ .l-flex-col.u-pd[ .c-img.u-pd.u-center[![](im/case-study-arc-move.gif)] .u-center[*Arc move from one location to another*] ] .l-flex-col.u-pd[ .c-img.u-pd.u-center[![](im/case-study-arc-transition.gif)] .u-center[*Arc transition in which the center of the context moves along an arc path.*] ] .l-flex-col.u-pd[ .c-img.u-pd.u-center[![](im/case-study-choreographed-transition.gif)] .u-center[*Choreographed transition to create a perception of contextual expansion*] ] .l-flex-col.u-pd[ .c-img.u-pd.u-center[![](im/case-study-dialog.gif)] .u-center[*A dialog that expands from a location and reveals contents*] ] ] ??? - Dans ses guidelines, Material design définit quelques concepts applicables aux animations : montrer une continuité entre 2 interfaces, créer de nouveaux composants en réaction à un événement, montrer une relation spaciale entre 2 composants, etc. Avec des easing et des durations pré-définies. - Le projet Material motion est de créer un catalogue de mouvement pour construire des animations et favoriser leur cohérence. --- class: c-slide-list-lg # 2. Modularity Material Motion: "*eliminate the concept of a design/engineering “hand off” by encouraging both designers and engineers to think of motion in a common language*". .c-img--md.u-center[![](im/Handoff-v1.svg)] .u-center["V1 ideal journey"
The "handoff" is still here, but the Material Motion language helps the motion designers to describe their concept. [Source](https://material-motion.github.io/material-motion/user_journeys/motion)] ??? - L'idée est qu'en définissant un langage commun et des mouvements prédéfinis, il sera plus facile de tester des animations et de les mettre en production. - Pour cela Material Motion mise sur les Observables car ils sont facilement manipulable, reactif et "highly-coordinated" --- class: c-slide-list-lg # 2. Modularity Material Motion: "*eliminate the concept of a design/engineering “hand off” by encouraging both designers and engineers to think of motion in a common language*". .c-img--md.u-center[![](im/Handoff-v2.svg)] .u-center["V2 ideal journey"
There is no more "handoff", the motion designer (or anyone else) directly use the Material Motion language to prototype the concept. [Source](https://material-motion.github.io/material-motion/user_journeys/motion)] ??? - L'idée est qu'en définissant un langage commun et des mouvements prédéfinis, il sera plus facile de tester des animations et de les mettre en production. - Pour cela Material Motion mise sur les Observables car ils sont facilement manipulable, reactif et "highly-coordinated" --- # Beyond .l-flex-row.u-top-align[ .l-flex-col.u-pd[ ```javascript var $target = document.querySelector('...'); ``` With Event listener: ```javascript document.addEventListener('mousemove', function(e) { $target.innerHTML = `(${e.clientX})`; }); ``` With Observable subscription: ```javascript mouseMove$.subscribe(function(val) { $target.innerHTML = `(${val.clientX})`; }); ``` ] .l-flex-col.u-pd--md[ .u-large[Issues in both cases:] - DOM dependency (read / write) - Multiple event listener on the same component - Conflicts (overwritten transform property, media queries not considered, ...) - Performance ("the DOM is slow") - ... ] ] --- class: c-slide-list-lg # Functional Reactive Animation with CSS - David Khourshid (@davidkpiano) - Using **CSS variables** with **JS Observables** .c-img--md.u-center[![js to css](im/observables-to-css-variables.png)] ??? - L'idée derrière est de véritablement séparer les rôles - Les Observables capturent des événements et émettent des valeurs - Le variables CSS les retranscrivent sur le front --- class: c-slide-list-lg # RxCSS .l-flex-row.u-top-align[ .l-flex-col.u-pd[ ```javascript const mouse$ = Rx.Observable .fromEvent(document, 'mousemove') .map(({ x, y }) => ({ x, y })); const style$ = RxCSS({ mouse: mouse$, }) style$.subscribe(...); ``` ] .l-flex-col.u-pd[ ```css :root { --mouse-x: 0; --mouse-y: 0; } .ball { transform: translate(calc(var(--mouse-x) * 1px)) translateY(calc(var(--mouse-y) * 1px)); } ``` ] ]
.u-center[Elements from [http://slides.com/davidkhourshid/reactanim#/](http://slides.com/davidkhourshid/reactanim#/)] --- class: c-slide-list-lg # Functional Reactive Animation with CSS - CSS Variables are easily debuggable in the dev tools - Maintainable - No excessive DOM manipulation - DOM node independent - Full power of CSS: pseudo-selectors, media queries, calc(), theming, ... - Easy fallbacks --- class: c-slide-list-lg # Functional Reactive Animation with CSS - "Alex the CSS Husky" by @davidkpiano - The reactivity to the mouse is given by a subscription writing only in 2 CSS variables (--mouse-x and --mouse-y)
See the Pen
Alex the CSS Husky Reactive
by David Khourshid (
@davidkpiano
) on
CodePen
.
--- class: c-slide-list-lg # What is left to do? .l-flex-row[ .l-flex-col[ - Experiment RxJS - Design animation - Experiment RxJS - Agreed on concepts and on our own motion catalog - Experiment RxJS - Play more with CSS custom properties... - ...and PostCSS! - Follow Material Motion project evolution - Experiment RxJS ] .l-flex-col--33[ .u-center.u-large[Let's go !]
.c-img--sm.u-center[![](im/dive.gif)] ] ] --- class: c-slide-list-lg # Some sources - [David Khourshid, "Reactive animations with CSS variables"](http://www.codechannels.com/video/jsconf/javascript/david-khourshid-reactive-animations-with-css-variables-jsconf-iceland-2016/) - [Ben Lesh, "Advanced RxJS: State Management and Animations"](https://www.youtube.com/watch?v=X_RnO7KSR-4) - [André Staltz, "You will learn RxJS"](https://www.youtube.com/watch?v=uQ1zhJHclvs) - [André Staltz, "The introduction to Reactive Programming you've been missing"](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) - [Sarah Drasner, "A Comparison of Animation Technologies"](https://css-tricks.com/comparison-animation-technologies/) - [Tyler McGinnis, "Imperative vs Declarative Programming"](https://tylermcginnis.com/imperative-vs-declarative-programming/) --- class: naked, middle, center name: thanks # Thanks