 Hello, again. I do believe in the last video I promised I would show you the video player of the, well, there's not massive amounts to show you, but I figure I would show you anyway, because it's a little bit different, I suppose. If you look on screen, I've got the video player, or I have a video player, and it's going to play. Oh, good. We can't hear me, thankfully. And when you go to a different section, it squishes down like that. And if you go back to the section you were on, it kind of restores back up to full size, or if you go somewhere else, and there's another video. When you start that, it replaces the one you were on, and that behaves in the same kind of way. You can do that, you can go back, whatever. OK, so I shall show you the code for that, which is, as I say, it's not a massive amount of code. In cds.js, when I go to a new section, the unchanged, if you remember the unchanged, I basically call this toggle small player if needed in the video handler, which is a static function on the class. Toggle small player if needed, there we are. And I'm basically looking for an iframe, because if I see an iframe, then I know that you've started the YouTube video, or at least you've gone past the picture with the button to press. We've embedded the iframe. So I treat that as a fairly strong signal. On mobile, it's a bit different, because even if you, I can embed with autoplay, and that will work on desktop, but on phone, it just puts the video player there again. It's not great, but at the same time, it's not a disaster either. I don't know at this point how many people are going to be watching the live stream on a phone versus on desktop. And if they're on a phone, it's an extra tap, which is not great. It's also not a disaster. Anyway, if you have an iframe, this is where we got to. If you have an iframe, then it's playing. And it's basically if the current window location matches the href on the playing video container. So when I embed the video, and you kind of press play, I stamp into the data attribute on the video container. Let me show you. Let's do inspect, and over here in the video player. There we are. You see there's data YouTube ID, which is the YouTube ID, and data href, which is the location, the href of the page where that player was started. So if the two match, the current windows href and the href that I set, then we disable the small video player. And if not, then we enable the small video player. And if there's no playing video, then this will just do an early exit. So I think the interesting thing, though, is the technique of the animation. I have mentioned on many occasions before, and I probably will continue to mention it while it's still relevant, that animating transforms and opacity is typically the way to get decent performance out of your animations. If that's news to you, then you should have a read of the Google Web Fundamentals Rendering Performance section, or take the Udacity course that is also linked on there. And I'm sure we can pop it in the notes as well. But what we don't know, because it's a dynamic animation, it's a responsive site. So we don't know what the transform needs to be, because it could be, you're going from wherever the thing is on screen down to the bottom corner, but we don't know specifically what that transform would need to be if we were going to use a transform. So I use a technique called flip, and we have a supercharged TLDW on flip. The idea is that you can calculate the transform dynamically by saying, well, where is this thing now? And probably in that kind of big, masked head area. Oh, that's where it is. And where do I want it to be? Well, I know that, because I want it to be like 24 pixels from the bottom right-hand corner of the screen, or 32 pixels, whatever, and 32 pixels, yeah. And I want it to be 40% of its actual size. So you see what I do is I call here getBoundingClientRect, which is the get me the current position of this thing. And getBoundingClientRect has been around for ages, like, i.e. 5, 4, 5, 6, it's been around forever. It's been ages. And you get back this object with width, height, left, right, bottom, all that kind of stuff. And then we basically make all the mutations that we'd need to put it into the bottom corner. And then we ask the question a second time, where are you now? What size and everything have you got? And so we know where we were, and we know where we are. We can now figure out what transform we actually need to apply, which is like the right position from the first to the right position to the last. And so what we actually do is we've left it in that smaller bottom corner position, but we transform it back and up and out again so that it looks like it's still back in its first position. And we do all that really quickly. This all happens synchronously. So you won't actually see a frame being shipped in between these steps. And the danger point is actually this. The second time we call getBoundingClientRect, there's a moment where the browser goes, well, you've just changed a bunch of styles here. I don't know where it is. So I have to go off and apply all those styles, which we call a forced synchronous styles. Forced, I think that's a forced, yeah, forced synchronous styles, yeah. And then a forced synchronous layout pass, which is like, OK, I've applied all those styles synchronously. I couldn't do that when I suited me out to do it right now. And I have to do layout, figure out where everything is and what size it is. So if it's a big DOM, that's this point here, the second time you call getBoundingClientRect, could be very expensive. In my case, it's a small DOM, and it's a calculated risk on my part. Anyway, so we've transformed it now back up, right? So it looks like it's in the start. And then this is the bit I actually really, really dislike about the platform at the moment. You have to wait frames. So you make all those changes. And you apply that transform that brings it back up to look like it's at the start position. And then you think, OK, I could switch on animations and tell it to go down to the bottom. But that doesn't work because browsers defer all these calculations until the end, unless you ask for something up front. So the polite version, which is what I'm doing here, polite, is to call a request animation frame and be like, OK, I'm just going to wait one frame while you apply all those changes that I just made. In this case, it's just that transform. I'm going to wait one frame for that. And then I'm going to, because this could have happened part way through a frame, I wait one frame for that to take hold. And then I'm going to wait another frame. And I'm going to switch on animations. And then I'm going to wait another frame. And I'm actually now going to transform the, like, basically get rid of that translation and the scale to put it down back into the corner where I want it. And that kind of wait a frame, do a thing, wait a frame, do a thing, wait a frame, do a thing, it's an artifact of the web, which I find a bit bleh. And I think web animations would probably help here. Because the other thing I have to do, well, the other way to do this, rather than just kind of waiting a frame, is to force it by asking for offset width or offset height. Because that will force the browser to kind of apply the change. And then I can make another mutation after that. And then I can ask for offset width again. And it would force it to make the change. It would force it into the kind of reflow sort of that read write, mutation, read a thing, cycle. I'm not doing a particularly good job of explaining that. But the idea is that I can force its hand. If I do, if I wait a frame with request animation frame, I know those tasks will happen anyway, because they'll happen at the end of the previous frame. So I can sort of, I can wait a frame, a frame, and then a frame to do it. Or I could have interspersed kind of faux read calls in the middle. And because of the fact that you could save it after this first frame, this first request animation frame, you could actually go back. And that could then restore, we could switch across to setting the player back to large again, rather than small. So I actually have to manage state a little bit here and kind of go, if for some reason they've gone back to the big player during waiting for these few frames, every time I have to wait a frame, I basically go, if things have changed in the interim, it just canceled. Don't worry about setting these next things. And that's, I mean, this is not code that I like writing. And I might, I might go to the less polite version where I don't use request animation frames, and I just interspers read calls to kind of force the browser's hand. I don't like this very much. So yeah, I'm obviously probably being quite clear that I don't particularly. This is probably the bit of code in this whole thing that I'm like, oh, oh, really? No. Also at the same time, it's not the worst code I've ever written. I have written some awful code. Oh, well, we're all going to move on, haven't we? Right. So that's taking the video down to the small player. You probably won't be surprised that you pretty much are going to see the same thing in reverse when we disable the small player and go back to the big, which is, again, you sort of take a reading of where this thing is, remove everything that made it go down to the small player in the corner, ask the question again, where are you now? And then there's a little bit of a dance asking for existing transforms because of that. And then it's frame, frame, frame, the frame dance. And then we are done. So there you have it. That's basically it in a nutshell. Then there's a lot of boilerplatey stuff about making sure that we can embed a link and that when we begin playback, we do an iFrame and we embed the YouTube video and stuff like that. So most of that is boilerplate. I think it's just the technique of creating the transform on the fly that's quite interesting. So there you go. I think probably in the next video, I might talk a little bit about the side now, maybe, because I've done a little bit more work on that. You might be noticing that I do a little bit. I bounce around when I'm working on these things, like sort of what takes my fancy today, because that's one of the freedoms I have in this particular project. The deadlines are there, but within that, I can still decide which direction I go and at what point. And I'm feeling pretty OK about it. I'm feeling actually very OK about it. I'm actually really enjoying this build. It's been a good build so far. Oh, I want to let me show you this little thing here. If I just enable this scroll to, when you go from section to section to section, let's say you go back to that. I pop the screen to the top, because that's kind of more what you'd expect from the behavior of it from a site. But on the body, on the body here, I have scroll behavior smooth, which you'll notice is currently an unknown property name. In Canary, if you switch on the flag, it switches on the smooth scroll, which is already enabled in Firefox. They've already got it. Oh, oh, do you need any dance? I don't think I am going to set it as my main browser. Thank you. Tempting. OK, let's see if that works. They've removed clutter, so I can focus on what I want That's kind. Right, when we go to a section, oh, it's not working. Do I need a new version of Firefox? Let me see. Firefox is up to date. Interesting. I'm wondering why I'm not seeing the smooth scroll. Maybe I did something different than. In which case, I'll save that for another time. Just goes to show, don't do a demo on the fly when you're thinking, hey, that all went well. Why don't I just show them a little bit of extra stuff? Don't forget that you can subscribe. Don't forget that you can check out the codes on GitHub. As always, comments and all the usual stuff below. And I will see you next time.