David Khourshid

Reactive Web Animations with RxJS

Conference talk at Nordic.js 2017. 29:15 long, 2.6K views on YouTube. Topics: reactive programming, css & animation.

Watch on YouTube (Nordic.js)

What is this talk about?

David Khourshid gave this talk at Nordic.js 2017. Read more at (From the video description.)

“You might be thinking, this is declarative, and that's true, so this original paper discussed ideas of creating declarative, reactive and functional animations.”

“Just like a promise, so consider the idea of promises where things happen asynchronously and apply to multiple values, and you get an observable.”

From the opening of the talk.

Key takeaways

  1. You might be thinking, this is declarative, and that's true, so this original paper discussed ideas of creating declarative, reactive and functional animations. 4:00
  2. As you know, the combined observable, then the top one is going to be caring the latest observable values from the second observable. 18:00
  3. Actually, you could test the animations because you're you're just testing observables, and there's a ton of good resources on testing observables, especially in RxJS. 26:00
  4. If you take one of two things from this talk, it's either do reactive animations in your app or just learn RxJS. 28:00

Quoted from the transcript.

Full transcript

From YouTube auto-captions. Punctuation and capitals were restored automatically. Technical terms were normalized; some errors remain. 4,215 words.

0:00 Introduction

We all know that animation is a crucial part of the user experience for both web native and any other visual medium that we could have. I studied piano in University, and music is basically animation expressed through sound, and not through, you know, visual arts. And there are so many animation libraries nowadays that focus on the animation from point A to point B. Having a start in an end. But what about the animations that basically have no timeline, no fixed timeline? I'm talking about animations that can respond to user input or any other external input, or even other animations. So that's what I'm going to talk to you about today and hopefully learn a little bit of RxJS if you're not familiar. My name is David Khourshid and I work for Microsoft. Recently, we launched project Glimpse. This has nothing to do with the animation, but I sort of sneaked in a couple little animations into this app anyway.

If you work on Node and express, I highly encourage you to check this out. This is something that you add to your Node and express apps, and it allows debugging and diagnostics into your full express application. So check it out at no dock at Clint's com shameless plug. So anyway, as they said, I am a pianist, I promise, and I have a dog. It's a very special dog and I live in Florida, which is right here, you know. Just for the geographically challenged Sweden is way the [ __ ] over there, which I'm really glad to be in Sweden, you know, because we have a little problem that's working. Irma and hurricanes are notoriously difficult to track because they're big, huge, dangerous things that, you know. You could think of a hurricane as a continuous sequence of events of rain, wind, thunder and just other destruction. And so we measure it using satellites, using radar and spy, flying reconnaissance airplanes into the eye of the hurricane every three hours.

This means that we Floridians, we get advisories every three hours, and based on each of those advisories. You know, we have a different reaction because the path of the hurricane updates and so on. So now the path of hurricane Irma is headed directly to my city, Orlando Florida, right in the middle of the state. But it's still gonna. Get the hurricane, and ironically, it's, it's coming in just as I'm supposed to fly in.

Play from 0:00

3:00 You might be thinking, this is declarative, and that's true…

So, you know, thankfully I have a place to stay. I called them, they said that they have plenty of beds, so I'm like, perfect, okay, so what are we? Active animations? You might be thinking, you know, does it have anything to do with? React does. Isn't it? Just like a normal animation? Here's how it was defined in 1997. A reactive animation is one that involves discrete changes due to events, and these events could be continuous events. So by discrete, we mean things that can exactly be measured in continuous. Just the more you try to measure it, the more infinite you realize the data you're trying to measure becomes. But one of the useful things in this paper that was said was that by allowing programmers to express the what of an animation, one can hope to automate the how. And so, you might be thinking, this is declarative, and that's true, so this original paper discussed ideas of creating declarative, reactive and functional animations.

And so he called it Fran was the basis for RxJS and reactive Accent, all of these other, you know, the concept of functional reactive programming? So why do we want to make reactive animations? Well, if you, if you look at the top apps, you look at just anything that is on the app store that, you know, has any sort of popularity. You're gonna notice that. A great deal of focus has been placed on the actual animations inside the app. And this is to capture user attention and to make the user more engaged with the product.

Play from 3:00

5:00 Observable SU can be merged together so we could compose observables…

So these are extremely good things to focus on. But before we get into the actual reactive part, I sort of want to explain to you what type of data structure that we're going to be dealing with. So in JavaScript, we have a value, which I'm just representing as a ball now. A value could be like a string or a number, or an object's just any single fixed value. But now let's that time into the mix. Tomash gave a really good talk yesterday about just the different ways we handle async. And promises were one of them, so a promise can be considered a single value over time now. Of course, we have this idea of multiple values, and that's arrays, and all of these values occur at once. An observable, which is what we're going to be dealing with, can be thought of as an array. Because it's essentially a collection of values that happen over time.

Just like a promise, so consider the idea of promises where things happen asynchronously and apply to multiple values, and you get an observable. Sometimes I like to refer to them as dreams, because it's a little, a little bit more approachable that way. So, an observable can be considered a collection of values that happens over time, and observables are immutable, which means that you could subscribe to be observable, omitting each value. But you can never go back and change the value, it's just like time, you can't go back to the past and change time. Otherwise, we would have done so in November in the United states, but I shouldn't said that anyway. Observables can do one of three things, Bacon completes, which means the observable is done amazing values. They can be indefinite, which means the observable is going to keep emitting values. And you're never going to know when it's going to stop. Or an error can occur, and that's sort of the same as a an observable completing, but instead of a completion, it's an error.

Now. One of the most popular implementations of observables is known as RX.Js, which is the an implementation in JavaScript of reactive X. In order to get it to npm, install' RxJS, save it because you're gonna want to keep it and then import our X from RX.Js. Creating an observable is also, I don't want to use the word straightforward. It's a bit of a learning curve, I'm going to be honest with you. So what happens is you pass in a function which takes in an observer. In this observer has three methods next, error and complete. For simplicity, we're just going to use the next method. So let's say we wanted an observable that only emits every second, right? So we set an interval, and on each interval we just passing an old value, and so that's going to emit every 1000 milliseconds. Now this observable, we also return something to clean up the observable, and that's clear interval, of course, the beauty about RxJS.

Is that there's operators for pretty much almost anything? So all that just becomes this our X dot observable dot interval 1000, and then you have that observable of values every second. So you could create an observable from an event, such as whenever you move your mouse on the document body, you could capture that mouse move events and then all of a sudden you have an observable, which you could think of as in a synchronous array of mousemove events. And then, just like a raise, you could map over those mouse moves and get the X and Y values, which it's climaxing. Client Y. Okay, so example time over here, I have this range observable, and this range observable is going to get input, yeah, input events from the range, and we map that to the value of it. So in this case, we have either six, five, four, three rooms. And so whenever we, whenever we get a new value, we're going to call this update function.

And all that does is set some attributes and then we could see that, you know, it changes. And of course, you might be thinking, well, yeah, but I could do this with a normal event listener, right? Yes, but we're gonna be talking about the advantages of doing it this way later on, especially for more complex applications. For example, you could create an observable, which is the same type of thing from an events pattern. And so this is not a strict DOM event, err, or browser events, but this comes from anywhere. So, Hammertoe, yes, is the library for handling touch input. And we could, we could use that to detect whether we're panning left or panning right. You could also do things like zooming and scrolling and other stuff, and then we could just pass in a callback. I call it handler over here, so that whenever we pan left or pin right, that handler is what emits the value.

And then from that, we could map each one of those events and again get the Delta X and Delta Y. So that's why I'm. Hammer is such a cool library, because it just gives you those values, so here's an example of that. I'm doing two observables here, IVA Stan, where I used Hammer, and so I'm detecting pan, let's pan right? In pan end, which is when I take my finger off, and I'm also detecting whenever I take my finger off. I want this physics library dynamic CAS to sort of spring it back to position, and so using observables it, you know, it works out in the very straightforward way. And it's also declarative. Because we're not doing anything until this subscribed method down here, so these observables don't even start listening until we call subscribe on them. So they're lazy by defaults, just like me, lazy. Come on, alright. So, as I said, there's a lot of operators in RxJS, and you might be familiar with a lot of them right off the bat, such as math, you know, you map over in the Ray, you're going to get the same type of result from.

Observables, filter works the same way as well, so over here we're just getting all the yellow balls now observables since they're async arrays. We could also have time operators too, and so in this case, every item that we got, we delayed it by 100 milliseconds. Now, you might be familiar with a radar reduce, we have a scan operator, which sort of does reduce, but for intermediate values. So to exemplify this over here. I have an observable of just numbers and then Scan is going to take the last number and the current number. Add them together and then that's the next value. And of course, we start with 0, so 1 plus 0 is 1, 1 plus 1 is 2 and so on. If you know Redux, this is a very, very useful operator to have, because that's Redux. In one line of code, one line of RIA RxJS.

Observable SU can be merged together so we could compose observables from other observables. In this case, we have two different observables and we could merge them into one. Just by combining their events together, we're gonna see how that's useful in a minute. These are called marble diagrams, these arrows with the marbles, and there's two very useful sites that I like to go to for visualizing them. One is Arcs Marbles Calm, which has been around for, I think, a year or two. And then one new one is called Arcs Viscom. Where you could actually input observables and see them live and so you could create. Your own, whereas Arcs marbles is fixed. Honestly, if I were to name them, I would have called them observables. But so, given that, I want to make it clear that.

Play from 5:00

14:00 Just like so, and that way we get goats that…

Animations are a function of state and time, so sort of like Redux, or representing your UI as a function of state. And in action, you get a new state, same thing with animations where it's a combination of time and actions. So, given the current state and given time and potentially other actions, you get a new state. By thinking of animations this way, we could approach animations in a functional way. Now there are many libraries for creating animations that sort of allow you to do it. Imperative lis in an easy way, but it's not really scalable, not really composable, and definitely not testable. I mean, how many of you actually write unit tests for animations? No one's hand went up great, so let's actually try to create something. Here's what I want to do, I want to do sort of an Apple TV effect with these goats. So the first thing I'm going to do is I'm going to get the mouse position and so I see that.

Like out that, all I did was get the from events mouse moves, and I mapped over it. Getting the client X in client Y, the next thing I want to do is take those X&Y positions and rotate the goats so that they face the mass. And so we see that we do that, and so these are two operators that we already learned from events and map. Now you could be saying, you know, we're done here. This looks great. I'm not satisfied quite yet. And here's why this is a cool effect for computer. But a lot of people they, they look at pictures of goats on their tablet as well. And so in order to get the same effect, we want to merge both mouse moves and touch moves as well, and so we're doing that over here. We're getting another stream of touch moves from the documents and then we could just combine the two and merge them.

Just like so, and that way we get goats that, you know, respond to both mouse moves and touch moves just by doing a single merge command. Now I'm still not satisfied, I. Sort of want a smoother animation, because as you could see the goats, they just sort of they sort of stopped. So, like, whenever you're done moving your mouse, it just stops, and so that's. That's not really a natural animation, so in order to remedy that, we're going to be using animation frames such as, you know, Requestanimationframe. Now, the way to do that in RxJS is a little bit interesting. We have, of course, our cell observable interval.

Play from 14:00

17:00 You could pass the values into CSS variables

If we set that to zero, you could consider that like an infinite amount of ticks, right? Just infinitesimally small. Or at least as fast as the browser can do it. But we want to schedule each of these intervals to only occur on animation frames. So you could think of our X dot scheduler about animation frame as just a scheduler. That says, all right, all of you in line. I'm going to only allow you to come through every sixteen point, six, six, seven milliseconds. And so we're gonna be using a new operator - which is this with latest. From now, this is going into a little bit more advanced RxJS. So I don't expect you to fully memorize this or anything, but just be aware that something like this exists. So with latest from all we do is we take all those animation frame ticks and it's going to carry the latest. The latest emitted values from whatever observable you passed in, and then you combine them using a function, so here's a visual way of how that works.

If you think of the top as the animation frames and the bottom. As you know, the combined observable, then the top one is going to be caring the latest observable values from the second observable. I don't expect this to make sense, just know that it works okay. So we're doing this so that we could do what's called linear interpolation. And again, this is a little bit confusing. I don't like showing like tons of code on the screen, so I'll try to explain this visually. Let's say we have two values, right? 200 and 300. You could consider this X and Y values. Instead of it jumping from 200 to 300, I wanted to smoothly go from 200 and then arrive at 300 eventually. To do that. First, let's calculate the. Distance, which is 100, then I have this ratio of 0.1, where I'm saying I want to take a tenth of that distance. And then using that tenth of the distance, which is just 10.

I want to move the current position closer to the next position, using only, you know, that, 10. And so we get that now. If we play with the ratios, we could see how this works together to smoothly go from point A to point B. The lower the ratio, the longer time it's going to take, you know, to get from point A to point B. And so if we use scan and we pass this lerk function in or linear interpolation, then we could see that. Instead of jumping from one to two to three, it smoothly goes to the next value and this is exactly what we want for smooth animation. So we combined it with whipped laced from, and we use LeRP. So just like a Redux reducer, we take the previous value and the next value, calculate the deltas and use that to bring it closer and closer to the next point. And then with that one line, we get this.

So as you can see, it's a lot smoother, and I could even just make this smaller. Wow, now it's really, really smooth, so these are some smooth goats, all right. And so this isn't just limited to movement. This is a really cool demo. Balloon. Lizzy Linhart, where when you move your mouse, the emitted values are then converted to colors, and so by moving your mouse, this chameleon whale changes color. And what's awesome is this is using a library that I created about a year ago called RX CSS. It's just a very tiny utility library. I don't care if you use it, don't worry, I won't be offended if you don't. But what it does is you could use observables that you create in Arcs GS. And you could pass the values into CSS variables. Where, if you're not familiar, this is just a way of communicating with CSS through JavaScript. So you could, you could literally set these values in JavaScript. And CSS will pick it up depending on where you where you set the value, and you could use it directly in your CSS.

So this isn't JSF CSS, this is just. A medium of communication between JavaScript and CSS. And so, the idea is that we take a stream of values. And instead of applying those values directly to the elements, we sort, abstract them into these custom properties.

Play from 17:00

22:00 You could compose these observables together and react to just about…

And then we could use those however we want. Okay, so here's another example this is using RxJS and Hammer JAS. I didn't do this. This was done by varying the shark. And he actually has a really good article explaining how this works, but it's just a simple drag-and-drop. But as you can see, we have the smooth animation, the linear interpolation so that it smoothly moves from point A to point B. And furthermore, there's this notion of keeping states where this element is either being dragged or not being tracked. So we have two different states, and of course, you could represent those states in sort of a Redux reducer fashion, where you take the previous state and in action, such as drag start or drag end, and you get the next state, okay? And so in more advanced scenario with this is in this where I recreated a dribble that I really liked. And this has three states actually here, that's one, two and three over here, and so this is also using RxJS to do a bunch of things.

First, it's keeping the state of whether we're in this navigation mode, this large NAV mode and the small NAV mode. And it's also handling the animations as well in order to smoothly animate from section to section, and also from states to state. And so thinking this way makes you starts to think a little bit more declaratively about how you do everything in your UI, whether it's animations or whether it's states. And so over here, I have a state machine, where I have three states start, nap on and NAV two, and then I have a mapping of actions to next states. And so if I do a pan up action when I'm in the start state, I'm going to go to nap. And if I do a pan up action Nab one, I'm gonna go to Nab. - what this means is that when I'm in Nap 2 and I do pan up, you know, nothing's going to happen. Actually, that was a bug, you just I'll pretend. You didn't see that, but you know, that's another one.

So so, yeah, now you might be thinking, still, why animate with observables? I mean, you might be thinking, these animations are really cool. But I could just, you know, just stuff a bunch of code in events handlers, or I could go to my comfy imperative programming, whatever. But there's a few reasons, three, in particular, reactive animations using RxJS or using observables, or just this idea of functional reactive programming is declarative. Instead of saying, do this, do that, go there, go to there, in three seconds or so, we can say what we expect. For example, we could say you are a observable of mouse movements or touch movements. And this is a mapping of those mouse movements to coordinates, and this is an interpolation from coordinates to smoother coordinates. They're also composable so you can make an observable out of just about anything, including promises, arrays, single values, or there's even our Excel observable dot never, which doesn't emit any values. That's pretty useless, I never use it.

And so, you could compose these observables together and react to just about anything, including other observables, which means animations can react to other animations, and then you get these really complex animations. They're also immutable because these three things form, you know, just functional programming principles. And this way you could. Actually, you could test the animations because you're you're just testing observables, and there's a ton of good resources on testing observables, especially in RxJS. But now this gives you the power to test animations. To assert that when I do in the animation, from here to there, it's going to do exactly what I want, and it's going to be exactly where I want it to be. Also, reactive X is not a framework, it's not like React Angular review. Instead, you could use RxJS in coordination with any of these frameworks and more. I know Angular dot, IO, or whatever it's called, uses it by default.

And there's also integration with View and React. And lots of people have been using RxJS with all of these frameworks too. Furthermore, there's implementations in all of these languages, so the really cool thing is that you could use RxJS and what you learn from there, almost the same API you could apply to any of. These other languages, because reactive X is pretty much a standard, it's a community standard, it's not an official standard, but it is a standard nonetheless. Also, there's a library that you might not have heard of. It's called Material Motion, and it's something that some really awesome people at Google are working very hard on. And it's it's using the notion of observables to actually enable designers and developers to communicate on the same language for creating reactive animations. And so, whether you're doing work on iOS, Android or the web, the amount of handoff time between designers and developers is now cut.

Because instead of designers prototyping, since it's a declarative approach, designers could essentially code the animations and the developers could just tweak it a little bit and implement it into the applications. And so I really encourage you to check this out. There's some exciting stuff coming out. I think iOS is a ready version one, Android and web are in the works, so look for that coming soon. Really cutting-edge stuff. So I want to thank you and leave you with all these resources. I'm going to put this slides up. If you take one of two things from this talk, it's either do reactive animations in your app or just learn RxJS. Because you could do a lot of really cool things with it. Thank you, very much, thank you.

Play from 22:00

Related talks