We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Living Animation

00:00

Formal Metadata

Title
Living Animation
Title of Series
Number of Parts
27
Author
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language
Producer
Production Year2018

Content Metadata

Subject Area
Genre
Abstract
Most animation on the web is canned, non-interactive, and inflexible. It doesn’t dynamically adjust itself to changing conditions and user interactions. It’s expensive to create and maintain because the programmer or designer is trying to micromanage the motion. It needs to be hand-tuned for each new feature, device, or media query breakpoint. There is an alternative: we can think more like game designers. We can build robust, dynamic systems that animate gracefully, with clear separation between app logic and app motion.
VideoconferencingLinker (computing)WordCubic graphRoutingCurveSinc functionUniverse (mathematics)Computer architectureRight angleGroup actionCartesian coordinate systemTemplate (C++)Slide ruleAffine spacePoint (geometry)Video gameTransformation (genetics)Connectivity (graph theory)MereologyMathematicsWeb 2.0Disk read-and-write headMedical imagingDarstellungsmatrixSocial classMultiplication signDivisorReplication (computing)XMLComputer animation
Slide ruleEmailCore dumpObject modelTemplate (C++)RoutingSystem callAvatar (2009 film)Process (computing)AlgorithmVideo gameTrailLevel (video gaming)Combinatory logicMultiplication signTouchscreenTerm (mathematics)Right angleWindowFocus (optics)SoftwarePhysical systemProduct (business)Key (cryptography)Software developerConcurrency (computer science)FeedbackReal numberSineSound effectFrame problemCodeNeuroinformatikGroup actionDifferent (Kate Ryan album)PixelRule of inferenceWeb 2.0WebsiteTwitterLibrary (computing)SpacetimePhysicalismCartesian coordinate systemReal-time operating systemMaxima and minimaMotion captureWordVideoconferencingMathematicsBitFlagInheritance (object-oriented programming)MassWeb applicationSequenceMedical imagingEndliche ModelltheorieFamilyOpticsAreaPrisoner's dilemmaDigital electronicsGoodness of fitMaterialization (paranormal)Computer-generated imageryMonster groupData managementState of matterElectronic program guideMappingForm (programming)SummierbarkeitError messageZoom lensLimit (category theory)Harmonic analysisDataflowRow (database)Computer animation
Cross-site scriptingLiquidGame theoryFlow separationLogicRule of inferenceModule (mathematics)Random numberDefault (computer science)Game controllerFunction (mathematics)Trigonometric functionsUsabilityClique-widthGame theoryFlow separationLogicSet (mathematics)Library (computing)Mobile appBookmark (World Wide Web)LiquidGreatest elementModule (mathematics)Group actionRule of inferenceWeb 2.0Online helpState of matterOpen setWeb pageCASE <Informatik>BitTrailImplementationRight angleDifferent (Kate Ryan album)Zoom lensElectronic mailing listAdaptive behaviorPixelEvent horizonPoint (geometry)Domain nameView (database)Flash memoryPlastikkartePhysical systemArithmetic meanTerm (mathematics)MereologyLevel (video gaming)CodeGoodness of fitMathematicsNumberComputer animation2 (number)DataflowVideo cardWeb browserNeuroinformatikSoftware developerNormal (geometry)Normal operatorComputer hardwareVideo game1 (number)TouchscreenSheaf (mathematics)Parallel portLimit (category theory)Default (computer science)Formal languageGame controllerRaw image formatComputer iconGrand Unified TheoryCartesian coordinate systemStreaming mediaComputer virusObject (grammar)Extension (kinesiology)Metropolitan area networkIncidence algebraPhysical lawMedical imagingSound effectParsingBit rateTemplate (C++)Casting (performing arts)AbstractionTunisCondition numberReplication (computing)Endliche ModelltheorieElectric generatorServer (computing)Data structureTwo-dimensional spaceNatural numberProgrammer (hardware)Lattice (order)Phase transitionProcess (computing)BuildingInformation securityElement (mathematics)Computer animation
Game controllerDefault (computer science)Trigonometric functionsUsabilityClique-widthFunction (mathematics)Random numberBeta functionLocal GroupLevel (video gaming)Absolute valueBound stateObject (grammar)MathematicsPhysicalismLevel (video gaming)Dimensional analysisMultiplication signRight angleLine (geometry)MathematicsTerm (mathematics)Ferry CorstenRow (database)1 (number)Rule of inferenceUniverse (mathematics)Library (computing)SequenceOrder (biology)Functional (mathematics)Group actionGreatest elementElectronic mailing listCategory of beingMomentumTouchscreenText editorPattern languagePosition operatorPixelComputer iconSlide ruleState of matterFitness functionWindowMobile appSound effectExistential quantificationTime seriesLatent heatoutputTemplate (C++)Computer fontPoint (geometry)Reverse engineeringMedical imagingGraph coloringParallel portArithmetic meanVideo gameLink (knot theory)Declarative programmingGame controllerSemiconductor memoryGradientDataflowCASE <Informatik>Reading (process)Insertion lossElement (mathematics)TheoryIncidence algebraBit rateDivisorSpacetimeNear-ringCartesian coordinate systemMeasurementPhysical lawSubject indexingReal numberShared memoryWeightControl flowInterrupt <Informatik>Hydraulic jumpComputer-assisted translationComputer animation
CloningElement (mathematics)Connectivity (graph theory)Event horizonWeb 2.0BitMedical imagingInsertion lossMobile appControl flowTemplate (C++)Execution unitDrop (liquid)Existential quantificationDivision (mathematics)Computer animation
QuicksortRandom numberContent (media)Group actionAdditionComputer iconVacuumDemo (music)Division (mathematics)Hydraulic jumpDataflowCartesian coordinate systemMedical imagingPatch (Unix)Content (media)Real numberObservational studyComputer animation
Random numberBookmark (World Wide Web)Default (computer science)Game controllerFunction (mathematics)Group actionData modelArithmetic meanCategory of beingLocal ringState of matterLogicPosition operatorIncidence algebraBit rateInterior (topology)Right angleLine (geometry)Combinatory logicElectronic mailing listMathematicsCommunications protocolPairwise comparisonSystem callInformationLevel (video gaming)Connectivity (graph theory)Endliche ModelltheorieLibrary (computing)Template (C++)State of matterCellular automatonCategory of beingCartesian coordinate systemNeuroinformatikMassMedical imagingKeyboard shortcutWordCASE <Informatik>Sound effectGroup actionPoint (geometry)Multiplication signBookmark (World Wide Web)Context awarenessPivot elementLiquefactionInterrupt <Informatik>Sheaf (mathematics)CountingParticle system1 (number)ImplementationRoutingContent (media)Service (economics)Limit (category theory)GAUSS (software)Term (mathematics)Mobile appComputer animation
Electronic data interchangeConnectivity (graph theory)Software bugDemo (music)Slide rulePresentation of a groupLibrary (computing)Web pageMultiplication signMobile appRoutingComputer animation
Function (mathematics)Euclidean vectorModule (mathematics)VolumenvisualisierungBound stateSynchronizationSoftware testingAreaTouch typingDirection (geometry)CodeDemo (music)Software frameworkKeyboard shortcutSpacetimeNumberArrow of timeSoftware testingGroup actionDrag (physics)NavigationLevel (video gaming)Template (C++)Medical imagingLattice (order)Right angleSet (mathematics)Annihilator (ring theory)Theory of relativityComputer animation
Euclidean vectorModule (mathematics)VolumenvisualisierungBound stateSynchronizationAreaSet (mathematics)Element (mathematics)Content (media)Greatest elementEquals signWebsiteSource codeComponent-based software engineeringDemo (music)Software testingSoftware repositoryMultiplication signLibrary (computing)Key (cryptography)CASE <Informatik>Software testingLink (knot theory)Electronic mailing listBound stateKeyboard shortcutGame controllerNormal (geometry)NumberPresentation of a groupControl flowLengthSource codeWordSet (mathematics)Group actionComputer animation
Linker (computing)Daylight saving timeSquare numberAverageRow (database)Coma BerenicesBlock (periodic table)Data typeComputer animationXML
Transcript: English(auto-generated)
So, since I'm the last talk today and the only thing between you and happy hour, I think we're just going
to spend 30 minutes doing math. We're going to start with some affine transformation matrices, we're going to move on to some cubic Bezier curves. No. I wanted to sneak more math in, but I don't have time. There's too much to cover, so you're in the clear. So, the title of my talk is living animation
and I have a confession to make though, which is that I realized after I proposed that title that it's the wrong title for this talk. So, let me explain. My whole topic here is really that I think the way most people think about animation on the web is a little too static, a little too inflexible, and it makes it too expensive
and difficult so people don't do it and they don't do it well. And I think we could draw inspiration from the wider universe of things that are animated, video games in particular, for some of the techniques and architectures and ways to think about it that would give us much more robust, much more scalable, much more affordable ways
to put really rich experiences into our applications. And so, in my head, I've been calling those better animations living animations. And as soon as I started thinking a little more deeply about it, I realized living animation is totally a tautology, right, like if you look up what animation means, it means to give something life, right? All animation is living. That's the point of animation.
And if you think about it, we're talking about the verb to animate, but animate, the adjective, or inanimate, literally living and not alive, right? So an animated thing in your application is one of the live parts of your application. So it's not a good name, living. Instead, what we're gonna have to talk about
is a lot of affordances you have to do animation. So before I do a high-level tour of like big topics, I wanted to leave some practical teaser here for practical-oriented people. My slides are in mbrab, that practice is what it preaches,
it's all online. So this right here is showing a component on the left side, that was the previous slide. There's a component on the right side, that's just this template. And I can move between my slides, and I can do the same little transition here between these two components, right? This is a route transition, those are the templates.
And I can even interrupt them or skip over one while it's going, and it'll adapt in flight. So there's your little preview, there's cool things here. But first we're gonna step back very, very far back
and look at animation in the broad, because there is a lot of art, and a lot of art in both the sense of telling stories and teaching ideas, but also art in the sense of technos, of craft, right? Like, the skill it takes to make things come alive that aren't really alive.
And a lot of those principles are timeless, and they span technologies. What's interesting, so obviously what makes them all the same is that they bring things to life, but I wanna focus on what makes them different, because that's what we're gonna need to zoom in on when we understand what is the appropriate tool, what are the appropriate design affordances
for an animation system for ambitious web applications. So when we think about something like Steamboat Willie, this is one of the earliest appearances of Mickey Mouse. In this era of animation, everything that came after it for a long while, the artist has to control every last, I almost wanna say pixel, of course, but it's not a pixel, right?
It's just a drop of ink. There's nothing there that they didn't make by hand. From this era, we get the word tweening, right? Because a more experienced artist would have drawn the key steps of the animation, the key frames, and then a team of other artists would fill in the gaps by laboriously drawing all intermediate states. Not that different from the way you might think about
setting up a CSS key frame animation, right? Only you've got a computer instead of a team of other artists filling in the gaps for you, right? Somebody like Gromit here, this is also animation with a completely different design affordance, right? This is claymation, so what the artist gets
to tell their story is radically different here, right? In particular, they're definitely not responsible for every pixel worth of the video that comes out at the end, right? They are able to take advantage of existing materials, manipulating light, right? All that kind of stuff that just falls out. So the buttons and knobs and levers you're pushing
to get the effect you want are radically different here. We move up to something a little more computer-oriented. Modern CGI, like this character Sully from Monsters, Inc., is not something that any artist could draw in detail. Nobody's gonna keep track of all 100,000 hairs on Sully.
That's a job for a computer, and so the way you animate this guy is you do not sit down and figure out how they're all gonna move, right? You write an algorithm that figures out how they move. You write rules, and you let the actual life emerge from the rules. Very different way of thinking about it. So, so far, we've been thinking things that are not as interactive.
If we move to the more interactive world, even a very basic level 2D video game like Super Mario Brothers has a lot more combinatoric complexity to it than a Pixar movie, right? Because here, the individual pieces are certainly created, handcrafted by an artist, but the combinations of them
are just infinite, right? There's an algorithm. It's probably possible to get a screen of Super Mario Brothers on your TV that nobody's ever seen before, because it just happens to be the right combination of pixels. What we see is rules controlling things, right? Nobody has to think about exactly how is it supposed
to look when Mario jumps off of that particular thing versus that particular thing. All they had to do was write one physics model, right? That doesn't change that much as you move up to a more modern era. The kind of things that Angry Birds does, it has a nicer physics model, so the artist's affordances are a little bit higher level. They're gonna, instead of having to draw literally
the flag going up and down in Super Mario Brothers, here they're just gonna say this piece of wood has mass x, and this piece of ice is this fragile, and let the physics flow. Most of when we talk about animation, of course, in our applications more practically, we're talking about things like helping guide the user's attention, helping them find their way
so they don't get lost. There's a lot of things that we take for granted, like the minimization here in OS X, that serve a really important purpose, and we forget it because we already know the things it's trying to teach us, but this is teaching you that when you minimize a window, where does it go, right? This is a great example of design affordances
using motion to teach. Otherwise, if you just click minimize and it disappeared, where do you find it again? You'd have to know that just out of nowhere instead of learnable. A lot of animations on the web are local and simple and just there to be adding a really nice,
charming effect. This one went around on Twitter very, very recently. It's really beautiful. I didn't create it, I just filled out the form. It's like a code pen. This is an example of, people often ask me, how do I use one of your animation libraries to do something like this?
The answer is don't. CSS animations by themselves are beautifully expressive for this kind of a thing. The giveaway is that there's not really any data changing about the situation. We have some examples right from our own
web infrastructure. There's actual nice animations in the navigation for the Ember docs. There's been a lot of nice upgrades on there. Good work, everybody. This is a great example of just using the smallest amount of animation. Why is it here? It's here because it lets you navigate through a much larger space than is otherwise possible.
This is another great example of you don't need a library to animate something like this. This is the basics. Shout out to the docs for Ember concurrency. This is the website for Ember concurrency. And this is a wonderful example of using animation to teach something that's not visible otherwise. It's making the invisible visible.
And it's because you can reach in and poke it and interact with it in real time and get feedback in real time, it's a really deep way to teach and learn. And that's the sine qua non of why you'd want to use animation. You're giving people a window into a world
where they can actually interact with it and get feedback. And that's a really powerful way to learn. And you might not think you're in the business of teaching, but everybody making software is in the business of teaching because you're gonna have to make products that your users can learn how to use. You're teaching them. You're teaching other developers how your code works all the time through your actions in the code,
through your actions in documenting and talking to others. So we all teach, and I always look for those opportunities to find great examples of teaching, and that was one. And finally, in terms of the coolest affordances for animation, and emojis are pretty hot right now, I hear. Of course, motion capture's not that new. It's just new that it's easy and cheap to put right in a phone.
So let's zoom back in and look at what are the affordances that are relevant in our applications? So a CSS transitions and CSS animations are two slightly different flavors of things that are just baked into CSS. And if you've never used them, you should try it out. They're very elegant, and there's nice tutorials
that I'm sure you could find online. They're great for a lot of the simpler cases where you just want to, you wanna manipulate stuff that's already in the page. Where they fall down and you need more is when you have situations where there's a lot of state change, a lot of content changing from one thing to another.
They're not as helpful there, and that's when you reach for some more powerful abstractions. Other things that people talk about as exciting are things like the Web Animations API. This is an open web spec. RequestAnimationFrame, various pure JavaScript libraries that do animation. All of these are good as implementation detail for a rich animation system,
but I would argue they don't attack the hard problem that I'm talking about today, which is once I've decided that I wanna move a certain thing from point A to point B, getting it there, any of these are a good way to get it there, and that doesn't actually make a big difference in the day-to-day life of a developer or a team. The hard part is actually deciding where it should go
and how it should get there and why. There's a higher level question that none of these tools really try to help you solve. So up until now, the best thing we've had in Emberland for this is LiquidFire, a library I started. It does a lot of this stuff in terms of helping you move from state to state. So if you're familiar with it,
you know some of what I'm talking about. The goal is very much to do that kind of thing, to have help figuring out what needs to go from place to place. But of course, if I thought it was done and there's nothing else to do, I wouldn't be up here talking, so this is gonna go into where we go beyond what's already available in LiquidFire,
particularly looking at some game engine-inspired principles or really strong separation between your app logic and your motion. So for example, if you've ever tried to struggle with these problems and realized you needed some aspect of your data, your actual domain logic in your application,
something in the code that's managing your state, to stop and wait for an animation to happen before it moved on, that's the kind of entangling that's no good. That's the kind of stuff that makes your app brittle, that makes animations expensive and difficult and hard to maintain, and it makes people wanna stay away from that part of the code. Instead, you really want to make sure that the domain logic of your application,
your models, the user's own data, the actions they're taking, all lives in one domain, and that the animation layer, the view layer, your rendering engine, is just doing what it can to follow along. It's essentially one-way data flow to the animations, not back out from them. And you're establishing rules
and letting the motions emerge. A lot of the way people approach animation on the web hasn't changed a lot from the Flash era days when people would very much hand-tune every motion curve, every bit of timing, down to the pixel of where they wanted the thing to be. And then it looks really nice, until you realize now it needs to look nice at four different breakpoints,
on four different browsers, and now we're adding a new feature that gets in the way, so now we've gotta redesign it, and now what happens if you're in the middle of it and three new events come in from the user and two from the server, right? It rapidly gets beyond the point where you can hand-craft this stuff, and we need more powerful systems
to help us solve those problems. So today I'm gonna be talking about a library called Ember Animated that I've been working on for quite a while. It's a glimmerization of liquid fire, meaning that just as Glimmer took the bottom layers of Ember, cleaned them up, made them really well-rationalized,
nice abstractions, nice low-level abstractions, that you can then implement Ember on top of in a more layered, extensible way that empowers innovation. This basically takes that approach to liquid fire. So it's a new layer that will be able to integrate it underneath liquid fire, so liquid fire's still a thing, but Ember Animated does a lot of the work of animation.
It's online, you can check it out, it's open, it's released. It's at like an 0.2.0 right now. This is a little throwback to Matthew's talk about module unification. The reason it's named Ember Animated is because once you're using module unification, you'll be able to say things like
animated colon colon each to refer to your component. It's a nice namespace thing. In a normal app, it'll just be an animated dash each right now. So let's do some, a little bit of teaching of the kind of stuff we can do. I've set up a very basic little toy app here.
This is an app for just keeping track of which Zoey's and Tomster's are your favorites. And of course, they're all your favorites. So eventually, this list will have them all, but they're getting there. And so all it really does right now is I have a list of these images. If I click one, I take them off the list, and if I click this button, I get new ones on the list.
And they're gonna get inserted. They're basically, the list is sorted based on the newest Tomsters at the top. So whenever I add a new one, it's gonna come somewhere in. Very simple, intended to be totally boring and normal Ember app. Here's what the template looks like. We're just gonna iterate over all the things and render an icon for each one. So the first thing we're gonna change is we're gonna switch to animated each.
Yes, there's animated each. That was like the number one feature request in Liquid Fire was for Liquid Each. We have it now. So I'm gonna set animated each. And the only other thing I'm changing is I'm just gonna say use transition. So this is coming from my controller in this case. Here's the controller, and here's the transition. And so as a first step, we're gonna import move,
which is a motion from Ember animated. And we're just gonna say kept sprites for each move. So now when I add a new thing, the old things are gonna move out of the way. So here's an example. There you can see some there. There's a new one coming in. Now we haven't done anything yet with the new sprite.
So you'll see that it doesn't pop in till the end. That's the default behavior if you don't do anything with it. And we also haven't done anything smart with removed things. So if I click one of these to remove it, everything else starts adapting and eventually the removed sprite goes away. It does the cleanup. So let's extend. So now I've added a new section to handle the inserted sprites.
So if you've ever used G3 to do animations, you might be starting to see some parallels here. There's definitely some parallel ideas that I basically discovered independently and then realized, oh wait, this is the same thing. Yeah, okay, makes sense. So I'm still gonna move all my kept sprites. Oh, I haven't actually defined sprite, have I?
I should do that. So one of the limitations in LiquidFire that I'm trying to address here is that you could make custom transitions do almost anything, but it really was put all the onus on you, the developer, to get that right. And it was really hard, so not a lot of people did it. Lots of people had success with the built-in transitions
and that's great, but I want to really enable really rich stuff to get built and shared and customized much more than it was possible before. So this is a whole language for composing new transitions out of motions. And instead of dealing with the raw elements, the raw DOM elements, you get them handed to you packaged up as sprites.
The term comes from the long tradition of 2D graphics. Really early computers like Amigas and Apple IIs and early gaming systems had actual hardware support for things called sprites because it was just that computationally expensive to move a little picture around on the screen. But our sprites, really our sprites, like we really have, you might not think of yourselves
as 2D graphics engine programmers, but that's totally what modern web browsers are. So you're moving sprites around. This just packages them up for you so that they are, they can do a bunch of things normal elements can't do. For one thing, a kept sprite knows it's where it started and where it ends already.
It has measured, the library has measured for you those things and so on that sprite, it has all those dimensions. So a motion like move, I don't have to tell it where to go, right? It's gonna be able to know that from just data carried in the sprite. Another thing is I can do anything to these sprites in terms of changing their styles and that'll all get automatically killed enough for me
at the end of the animation. I don't need to worry about it. That's why there's a sprite abstraction. And there's more to it and we can go into that as we get to it. So my kept sprites just move. They can do that on their own because they have a place to come from and a place to go to. My inserted sprites are new so they don't have a place to start. They have a final position.
We know where they're gonna go into the list but we need to give them somewhere to start from. So in this case, I'm telling them to start at pixel with X of the window width. This is basically saying start off stage right. So I'm gonna tell them to start there and then I'm gonna tell them to move and in this case, I'm gonna customize their easing which if you're not familiar with easing functions,
it's really just the idea that we want to give things realistic looking motion. In real life, things don't just suddenly start moving and accelerate from zero to 60 in no seconds at all. They want to gradually ease up to speed and so here's an example of customizing an easing function. So now my inserted sprites come in from stage right and they go in where and you'll notice
they'll line up in the list right. And I can keep sending many in concurrently and they're all doing the right thing. Last thing I need to do is give those my removed sprites. My removed sprites still don't do anything nice so let's finish by doing a very mirror image thing for the removed sprites to what we did for the inserted sprites. In this case, inserted sprites,
they already carry an end state and you need to give them an initial state if you want them to move. Removed sprites are the reverse. So they already have a start point because we know where they started in the list. We need to give them an end point if we want them to move. So in this case, we're gonna tell them to also end off stage right and we're gonna also give them an easing function.
And I've given these guys opposite easing functions because the general principle here is when something's going flying off screen, you want it to exit at full speed. And when it comes flying back in, you want it to enter at full speed. But when it comes to a rest on screen, you want it to decelerate gently to a stop and if it's accelerating off, accelerate gently. So that's why these are asymmetric easing functions.
So now we have everybody moving and we could bring a whole lot of sprites in and we could have them going in and out at the same time. And if you watch carefully, even the ones coming in will steer themselves over time to adapt the other ones coming in around them.
So where did all that stuff come from, right? We didn't say anything very complicated. Seems like a lot of magic going on here. Let's take a look. Wow, I have too many top shoes here. I need, I'm trying to get to them on my list. So let's pull in some tools here.
This little slider control, there's an Ember animated tools add on. You can add some extras that you don't normally want in an app but are really nice for diagnosing and debugging your animations. Let's just slow the world down and bring a bunch in slowly. Now let's go into like bullet time.
Now I can actually aim close enough to hit one and unfave it while it's still coming in. So if I click this guy and then turn time back up, he's gonna go fly out. Right, so where did all this momentum stuff come from?
Our transition function here, every time I click, my transition function reruns from the beginning, right? We haven't talked yet about, we haven't had to do anything that awaits. All of this stuff is running in parallel, so the fact that this is a generator we haven't gotten to yet, but if you're familiar with Ember concurrency,
you might be aware of this pattern. This whole thing's cancelable, right? So all these actions that I'm launching are cancelable and so when I click again, all the running animations are canceled right there on the spot and we start again with this transition function. So the reason it declaratively lays out these categories of sprites
is that all you need to do is make sure that every category of sprite is assigned. Every time there's a click, you're gonna do a new thing with that sprite and we're gonna assign it to a motion and the motions are able to manage continuity across from one animation to the next because when I start moving a kept sprite,
that motion has access to the history of what other motions were running on that sprite in the last animation. So all of the complexity of momentum, for example, can be implemented in a library function like move and this is just a built-in one that's very handy that comes, but this could come from an add-on, this could be things that we build up and share.
So, what happened when that sprite was coming on screen, he's still flying in, we're in the middle of an animation, he's an inserted sprite, he's brand new coming in from stage right. When I clicked on him and it caused everything to recompute, now he was in that,
or again, but now he's a remove sprite. Sprites move categories. So I only had to make the rule for remove sprites once that they exit to the right and that way once he's changed state from inserted directly to removed, he was able to turn around and do it. So this is an example of what I'm talking about in terms of having higher level affordances so that you're gonna be robust to changes,
changes that just people clicking buttons fast, changes to new data coming in, changes to new features in your application, changes to things like let's just change the layout of this whole app. So this is the same basic template with just new style sheets and also I've styled the time series a little differently so they take up more room to get a better feel for this grid.
Now it's just flexbox with wrapping so they form a grid. Same transition I could run so if I get rid of one, they'll just move and it works, not so pretty in this case, we can make it better. But I can do the things I was doing it for and even though I've completely changed the way my layout works, the animation still keeps up.
There's nothing list specific about the animate each we saw. It doesn't care. You can lay this out with flexbox, grid, could be SVG elements, it doesn't matter. Let's make a nicer animation. Here's what the animation looks like and then we'll look at how I did it. We click to remove. We'll get a nice iOS icon-like effect.
Running one row at a time. I like this one. This is definitely, this is how, like if you delete an icon from your home screen, this is what it does, right? It moves them up one row at a time. So this animation is a little longer than the last one. It doesn't fit on one slide but it fits on one screen in your editor.
I'm doing more things here now so I'm adding some new motions from the library. So motion is really just any rarely atomic change you're making to the universe so fading in and fading out are also motions that can apply to a sprite. And here I'm actually doing things sequentially. So I'm yielding
because I want to first remove all the removed sprites and fade them out. So instead of just moving on without yielding, I'm gonna await. All the motions return promises and so we can wait for them before we move on to the next thing if we want them to happen in order. In this case, we're gonna wait to fade out all the removed sprites and then for all my kept sprites,
I'm gonna start fading them in. The reason for that is that maybe they were fading out before and they've just changed state so they need to fade back in because again, things can get interrupted. Sprites can jump categories. And then I have some helper functions which we don't really need to spend a lot of time on but basically we're gonna look at that sprite and see is it moving vertically. If so, I need to bump it z-index.
And to see why, if we think about what happens in slow-mo, when a sprite is moving vertically up a row, it's gotta appear on top of things that came both before and after in the list. So if we didn't adjust it z-index, it would look funny. It would be interleaved. We really want the moving thing to look like it's really z-index in front of all the rest.
So this is an example of using sprite APIs to apply a style change and we don't need to worry about cleaning it up. As soon as the animation is done, it will be gone. And then finally, we're gonna group them into rows and then if we get to the bottom of the screen, we get stopped. We don't need to animate the ones way down the bottom. We're gonna await each row one at a time
and then finally let all the inserted sprites fade in. So it's very, just once you can read async JavaScript, it just flows. It's what it does. And something like is moving vertically is using the fact that this is how sprites know about their dimensions. They carry with them not just their physical dimensions
on screen but like what was its color before? What's its color after? What was its font size before? What's its font size after? That lets these emotions be really smart about adapting to where things are. So you might be wondering, is there a lot of fancy, sneaky cloning of DOM going on to make this work?
And the answer is no. None of these examples so far of the Tomsters and Zoies have done any cloning at all. This is all moving around elements that are in your regular app, just moving them around. I also haven't said yet but it's an important feature of what I wanted to have in Ember animated
and eventually make be the public API in LiquidFire, which is that all these animation components like animated each are completely DOM-less. They insert no extra DOM into your templates. So animated each is a true drop in replacement for each. There's no extra divs. It's purely behavioral and that's really powerful
because now your composition capabilities are way better. You don't find yourself limited by having DOMs you didn't expect there that break your CSS or DOMs that you really wanted an event handler there, how can you get it there? Instead, we want to favor component composition. So if you know a little bit about animation on the web,
you know I'm not cloning and I'm not inserting any divs, something's gotta give here. Like how am I holding your place while these things are animating away? The answer is I'm not yet in these demos. And so if I do an example where there's content below the Tomsters and Zoies, you'll see that while I'm animating, the content jumps. That's because all the stuff I'm moving
is taken out of the document flow. So we don't want that. And in any realistic application, you're usually gonna have a situation like this. Your animated stuff doesn't just float around in a vacuum. It has to live in a real static document flow. So instead of this, this is what we have now, all we're gonna do is swap one of our divs for an animated container.
All right. So that's enough now to get the right thing to happen so that the container adapts itself to the animators inside. The container and the animated each are aware of each other because there's an animator protocol. There's a service as an implementation detail of the library
where all the animators are wired up and they find their ancestors and they find their descendants and they chat. And so the container, for example, knows how long to time its growth because it knows how long the each is planning to keep animating. So that protocol's really powerful. It covers a lot of cases that are more complicated that you might not think of
until you try to actually use these things in a real application. Here's an example with nested animated eachs. So I have an outer list of collections and I could add a new one here. And then I could add some items to that collection. I could add some items to all these collections. I have two levels of eachs. I can add a whole lot of things to all of them.
And everything lines up. Even though some things could be in flight down here when I add new things up here and it moves everything out of the way, it's because the animators are all coordinating. And so even if your data hasn't changed locally, for example, this list here is some local list to this collection,
nothing changes there. It's just animating, the data's stable. The animator has no reason to change. But because of the protocol where they chat, they actually, any time any of the other animators are starting, there's a protocol where you can actually ask all the animators that are in flight, stop, go back into static position
where you're gonna be when this is all done. Now everybody can measure themselves and now start running again. And that allows it to dynamically adapt to a lot of these kind of situations. So, so far we've talked about, we've talked about eachs, right? Lists of content. Not everything's a list.
So here's a simple animated value, right? This is a thing you could totally have done with LiquidFire. We used to call it LiquidBind. Bind is kind of an old ember word now that we don't use anymore. I think value's a clearer term for what this is. So this is the animated value. Biggest thing this gives us that we didn't have a really nice implementation of in LiquidFire is, again, it has really powerful
interruption capabilities where if I just slam on the button, right, I get all the nice things. And if I go one way and come back, right, I get it to bounce back. Um. Thank you. So, you know, I said not everything is a list.
Not everything's an each. Some of them are individual values. But it turns out that's not true. Under the hood, everything really is a list when it comes to animation. Because that's the only way we have so many states in flight at once here, right. And so it turns out because I've already taught you how to animate eachs, that's actually the complete case. There's nothing new to learn for an individual value like this. You still have removed sprites.
You have inserted sprites and you have kept sprites. So like, when I do something like it's animating from 29 to 30, and we decide to go back to 29, right, when I click that button and go back, 29 was a removed sprite, it becomes a kept sprite. And the motion that we've applied here
can actually do the right thing for that case. So, animated value is really just kind of a nice short hand on top of an animated each. Same goes for something like animated if, that's in here too. So, so far we've been looking at one animator at a time,
or at least animators that were like maybe affecting each others position but not really talking beyond that. But the animator protocol goes further. So, one of the limitations of trying to add really rich animation in a design is that you always have needed to, sometimes you need to rearrange your architecture so that there could be an animation component at the top,
at the pivot point between two states. It needs to be driving the old state and the new state, and the animator has to be there the whole time. It's hard to have just a component on one route and a component on another animate to each other. Well, it was hard. So, this is an unanimated example. This is just my favorites are over here.
All the ones I haven't favorited yet are over here. So, if I click, it's gonna move from one list to the other, and right now they just jump instantly and move from list to list. Let's, and this is the template. It's just very boring and normal, right? It's two lists. Each favorite is mascot, each other's is mascot. When you click them, there's an action handler. The action handler's just gonna move them
to the other list by setting favorite to true or favorite to false, and my lists are computed properties that filter, right? So, I could do this, the very natural things you'd wanna do in an Ember app without bending over in any way. But then, I can add an animation rule. Oh, I'm still running super slow. Let's pick that speed way up, okay. And now, when they go from one to the other,
they go from one to the other, right? So, that's pretty fun. These also, of course, will bounce back. If I can click them. Oh, no, I'm not fast enough. Need to go slower. That was really slow.
All right, I should be able to get you, though. No, go back, okay. So, let's look at the really complicated transition logic you need here. No, it's not complicated, it's two lines. And the reason is, because there's this extra category of sprites we haven't seen before, which is called received sprites.
So, before we were looking at kept sprites, which have a start and an end position, and inserted sprites, right? If you think about these two components side by side, when I'm moving something off of one list and onto the other, it's a removed sprite for one and an inserted sprite for the other, right? Well, all the animators in their Gauss protocol compare notes.
And so, if the same data value, this is based on the data, right? The actual model here, not the component, not how it looks, not where it is. If the data matches up on both sides and is removed from one and inserted to another, they find each other and on the side where it's going to, it's no longer an inserted sprite, it's a received sprite.
And that means it carries with it initial information for its initial position. You can do the flip side thing and use sent sprites, which would be how the other side could animate. Generally, you only need to do one or the other if the thing looks the same on both sides. If it looks different on both sides, maybe on a fade between them, you might actually want to use both of those sprites and move them together.
So, now I've mentioned all the five sprite categories. This is all of them. Inserted, kept, removed, received, and sent. And so, it's really just a combination of what's their initial state, what's their end state, and these are all the combinations. So, this is far matching across components. Before, we saw it with two components
that were on the page together. Here, we have it across two different routes, but the same principle applies, right? So, I've demoed a thing like this before, maybe actually three EmberConfs ago.
That was way less robust than this. That was a thing you could do on a demo. This is a real thing. You can use an app, and it will work no matter how hard you hammer on it. That said, these slides totally be nice to me because it takes a lot of time to make your presentation as an Ember app, turns out. There's totally bugs in here. But that's more about the presentation
and not about the library. The library's quite solid. And again, these can go back again and change your mind. So, as a last demo example, last night, I was chatting with Sam Selikov of EmberMap,
and he had a real FOMO about how other frameworks had nicer drag-and-drop demo capabilities and stuff. So, I said, well, it's the night before my talk, but I could probably add another demo. So, this is the same Flexbox layout, the same template. It's just an animated each, but now I've applied a drag motion to it. And the only other thing I had to add
was some action handlers to kick off the animation when you start moving things. But I can just drag and drop them to new places, and it works. I've added a priority number on each one so that I'm just changing their priorities, and that's causing everything to reanimate. We could do this with touch just as well.
And because I love to keep tweaking things even to the last minute, after Melanie's talk this morning, I decided that this really should be keyboard navigable. You wouldn't want it not to be. So, I can tab through, and I can use the arrow keys to navigate in all directions to find the one I want.
I could push space to pick it, and then I could push the arrow keys to move it around. So, lastly, I just wanted to leave a note.
This is a lot of code. We're gonna go through it in detail, but adding lots of animations here, it shouldn't make your testing hard. It shouldn't make your testing brittle. And all the new testing stuff we have is so great for this. I was so happy to see it all landing. And it turns out you could do really great stuff, like set up animation test. Whoop, that's me hitting the wrong key.
Oh no, come back. This is my keyboard control biting me for changing it the hour before my talk. Just like set up rendering test that Ember gives you standard now, I can also set up animation test. That's gonna give me a couple of things like import time from Ember animated test support. So I can say things like time pause,
set a value, wait for Ember to settle, advance 10 milliseconds, assert that my flying sprite bounds are close enough to where I think they're gonna be, and then run the rest of the thing at 40 times normal speed and let it finish and then be done. So, animation settled, bounds, time.
These things make it really composable. Our testing story is still top notch. I'm very happy with where it's landed in Ember, and this is just a great use case for that. So, the next thing that takes Ember animation, Ember animated live is we need docs. There are none. Well, actually, so all of you now
are the most knowledgeable people in the world about it other than myself. So I am definitely interested in help here, and I've written a quest issue that breaks down a full list of all the things we need to burn down to get docs going. So that's our next step. Here's the links.
The first is my presentation that you're looking at right now. The second is the actual library. So that's all open sourced, and go forth and build cool things with it. Thank you.