2.4 How to Animate a Formula 1 Race
In the introduction video you’ll have seen a simple, animated representation of a Formula 1 race. Believe it or not, that was actually quite easy to do. In this final lesson I’m going to show you how to create that animation, step by step. Let’s get started!
2.4 How to Animate a Formula 1 Race
If you watched the introduction video, you saw that Formula One race representation right? Well, believe it or not that was super easy to do. And in this lesson, I'm going to show you step by step how to recreate it. So we start from this un-styled page, right? We have some content, we have a title here, followed by the top three contestants. Now, of course, I've simplified these. I only used three drivers, but you can use as many as you want. So we have our three drivers, each one has sort of a badge with his initials and also the team that he is driving for. And then, finally, we have the racetrack. Now, this is actually a pretty accurate representation of the Gilles Villeneuve Circuit. And I got it from an SVG file from Wikimedia Commons, this one right here. You can also find a link in the lesson notes. So what I did was I downloaded this SVG. I brought it over to sketch. But, I didn't want all of the components. I just wanted the outline of my track. So what I did, because this is an SVG, and because catch has this capability, I could go into the track itself. You can see it has a separate group here, right? And I can grab just the path, right? So I grab the path, I put it in its own artboard. I decreased the border thickness to just one pixel, and I made the artboard fit its width and height exactly. And what I did next was simply right-click on the circuit itself, and I hit Copy SVG Code. So with the SVG code, I then went into sketch and I pasted it right here. So what you can see now is this. This is an important part, g with an ID of circuit, and this is actually the entire path. And I wanted to do this, because it's very important to have a continuous path to do what we want to do which is to have the course just follow the path all the time. So this is the path. In here we just have the finish line. Yeah, very simple SVG. And apart from that, we'll just standard markup for the elements you just saw. Some headings, a div with a class of stats, and in each one we have a div with a class of stat with a unique ID. And then we have the circuit container, we have list of cars, yeah, also with unique IDs, car 1, 2, and 3. And that's basically it. So the first thing we're gonna do is actually give this entire thing a little bit of styling. So we'll start with the circuit container. Circuit container, what I'm gonna do is add width, the width of my SVG. I'm gonna set a position to relative, and set a margin of 0 and auto. So everything is centered nicely in the page. Next, let's style the cars. So I wanna say car, I'm gonna make them 30 pixels in width and height. And what's important, you need to set their position to absolute. Okay, let's also add a background color of red, just for testing purposes. And now, let's make the cars follow the path of the circuit. So I'm gonna say animate. I'm gonna target, let's see, targets car. But before that, actually, I'm gonna use a variable here, I'm gonna call it path = anime.path. This is a method from the anime object, which basically allows you to pass in an SVG path like this. So in here, I'm passing in this exact path. Okay, so now what I can do with this is I can say for each car, we're gonna move it using translateX to the x-value found in the path. And we're going to do translateY, it's gonna be path('y'), and we're also going to rotate the car based on the path angle, okay? So let's see if this works. I'm gonna refresh, and it does work, it's just that the animation is very, very fast. So let's add a duration of, let's say, 5,000. Okay, let's make it 15,000 so you can see. Okay so, they start from there, yeah, they follow the path there, and then they arrive at the finish line. But there's something off about how they move, isn't it? Right, you can see a little bit of a kickback there. Yeah, there it is, right here, and that's because the easing that was set is not right. So instead, let's set an easing of linear. All right, so now they start properly, and they follow the path correctly. But you'll notice that they're not actually in the center of that path. So to fix that, I'm gonna go right here and I'm gonna say top: -15 pixels, so half of their height, and also left: -15 pixels, half of its width. So now the cars are placed correctly in the middle of the track. Let's style these a bit further. I added font weight, aligned the text in the center, added a color of white and also gave a border radius of 5 pixels. Now let's go ahead and add a background color to each of these cars like this. For now, just skip this a bit. Yeah, I just added for some future styling. But now car-1, car-2, car-3, they each get a different color. So they currently look something like this. Now, each car drives currently at the same speed. But that's not good, right? Each car should have a different speed, so let's add a different speed results = to an array. And we're gonna pass in 10,000, let's say 12,000, and 15,000. Let's say that this is the number of minutes or seconds that each car had to drive in order to reach the finish line, right? These are completely arbitrary, of course. So for each of these cars to get the unique duration basically for the animation. For the duration I'm gonna use a function, and function, I'm gonna pass in a el, i, and I'm simply going to return results i. So now, each car drives at a different speed. And you're gonna see that the first one finishes the race in 10,000 milliseconds, the second one in 12,000 milliseconds and the third one in 15,000 milliseconds. All right, now let's add some styling to the rest of this page. And I am just gonna paste in some styling here and walk you through it. So I applied styling to the stats container, setting some borders, some margins, paddings, stuff like that. Each stat will then occupy a third of the available space. It's gonna be floated to the left and itself is gonna have a bit of padding. Finally, I styled the thumbs. And the thumbs basically are these bits right here. They're now positioned in the center, they have a width, height, a color, and a border radius. So our updated page now looks something like this. Now, I want to add some more animations to this page. For example, I don't want the race to start until I can animate these three drivers. And let's say that I wanted them to, each stat, I want them falling from the top with a nice easing. And I want to make them fall one at a time, not all at once. One way to do that is to use the delay and duration parameters that I showed you in the previous lesson, or we can use a timeline. A timeline allows you to create animations in series. So, you have one element to a timeline, and another, and another, and a specific element from that timeline will only be executed when the previous element or the one before it has finished. So, to create a timeline, let's start with the timeline for our drivers, right? Well, I’m gonna go right down here. For now, I’m going to comment this bit, and I’m gonna create a new variable. It's called raceTimeline, you can name it whatever you want, of course. And I'm gonna say ainme.timeline. Okay, so now I'm gonna say raceTimeline, .add, And .add is just like, or add is just like anime, you pass in an object with whatever it is that you want to animate. So in my case, the first target is going to be car car-1-stat. And I want it to fall from the top, so I'm gonna say translateY. And I'm gonna pass in an array -1000 to 0, this is something I haven't showed you this yet. By using an array, you basically set an initial value. So in this case, I'm saying look, execute or animate this property starting with the value of 1,000 and then get to 0. All right, instead of writing it 0, which will basically mean, just get to 0 from wherever you are. This allow us to set an initial value, and I also want to animate the opacity. So I want to take them from 0 to 1, from fully transparent to fully opaque. And I want this to happen over one second, all right? So let's see what happens. Yep. The first animation takes place. Now, I want to animate the second car stat and the third car stat. Well, I can simply chain these together like this, of course, we're gonna delete these semicolons. So we're gonna target car-2-stat, car-3-stat, and they're all gonna have the exact same properties. And then let's also animate the circuit container, all right? Because maybe I don't want to see the circuit container at first. So I'm gonna target the circuit-container, and I'm just going to animate its opacity. So now, we go from this to this. Okay, but actually the circuit is being shown by default, and I don't want that, so I think I just forgot to set, An opacity to 0 on this one. Yep, there we go. All right, now I want to start the race once all of these have completed animate. So to do that, I'm simply gonna add another object or another animation to the timeline. And I'm just gonna copy these. All right, let's have a look, so one, two, three, and now the circuit, and now the race starts. Let's add a small delay here on the race. Let's say that I wanted to start a second after the whole thing happens. Okay, pretty good so far. And now, what happens when the race finishes? Right, we want to show the winner. And to do that, what we're gonna do, in this case, Lewis Hamilton is the winner. What we're gonna do is we're gonna animate out the other two contestants. We're gonna bring this container to the middle, and we're gonna give it a special class. Now, to do that, to execute something after a specific animation has completed, we can use complete. And we're gonna pass a function, and inside that function, we can execute whatever code we want, right? In my case, I'm gonna do another time line. So, I'm gonna say winnerTimeline = anime.timeline. So I'm gonna say winnerTimeline .add, I'm gonna add first the circuit container. I'm gonna do the reverse, basically. So, let's see, I'm gonna copy it from here, okay? So I'm gonna do opacity from 1 to 0 this time, and I'm also going to add a delay of 1000. And then I'm going to add the number 3 and number 2 stats, so let's copy them from here. Yeah, so first we'll go with number 3. And what we're gonna do is simply do a translateX, we'll do a relative value here. We're gonna say + = 1,000, so we're gonna move it to the right 1,000. We're not gonna bother with opacity. Instead, I'm gonna add a delay of one second, and we're gonna do the same thing for car-2-stat, okay. And finally, for car number one which is the winner, yeah, we're gonna do the following. We're gonna set translateX to -50%, because we want to put this in the center. We're gonna set it's left to 50%, like this. We're gonna set a bigger duration this time, about 2000. And then, actually, let's see what this is doing, if it's working properly. So let's refresh, let's wait for the race. There goes the circuit, there goes three, two, and the number one, perfect. All right, so the last thing I want to do is add a class of winner to this container so it looks a bit different than the rest. On complete, we have a function and we'll simply say document, either query selector or getElementByID, it doesn't really matter. And we're gonna say, ('car-1- stat').classList.add(winner), right? So let's go ahead and style that class. What we're doing, basically, is adding a background color, a border radius, setting a transition for all elements and setting a pseudo element. Winner:after, where we set the content to WINNER, and we're positioning it absolutely on the top of our container, on the top and middle actually. So, let's see that animation for one last time. And there you have it, that's about it for our Formula One race, and also for this course. Now, I kept this demo very simple because I wanted you to follow along more easily and be able to focus on the actual animations instead of the styling. But, there is so much more you can do to this. You can add more drivers, you can add different circuits. You can add more options, you can add on screen controls for pausing, playing, restarting the animation. I strongly suggest you check out the official documentation in for anime.js and see everything that it can do. And with that said, I would like to thank you very much for watching this course. I'm Adi Purdila, and until next time, take care.