PAUL LEWIS: Hello again.
I do believe in the last video
I promised I would show you
the video player.
Well, there's not massive
amounts to show you,
but I figured I'd 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.
So I shall show you the code
for that, which is, as I say,
is not 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 kind
of fairly strong signal.
On mobile, it's a bit
different because I
can embed with autoplay and
that will work on desktop.
But on a 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 the
phone, there's an extra tap.
It's not great.
It's also not a disaster.
So 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 I press Play,
I stamp into the data attribute
on the video container.
Let me show.
No, let's do inspect.
And over here in
the-- where are you?
Video player, there we are.
And you see this data YouTube
ID, which is the YouTube ID,
and they 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 that
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
nobody 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 UDusty 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
super-charged 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 masthead area-- oh,
that's where it is.
And where do I want it to be?
Well, I know that because I
wanted to be like 24 pixels
from the bottom right-hand
corner of the screen or 32
[INAUDIBLE] or whatever.
And it's at 32
pixels, f And I wanted
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.
five, four, five, six?
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 of 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,
like 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 it, forced.
Yeah?
Forced synchronous styles?
Yeah.
And then the forced
synchronous layout pass,
which is like, OK, I've applied
all the styles synchronously.
I couldn't do that
when it suited me.
I actually had to do it right
now, and I have to do layout,
like figure out where everything
is and what size it is.
So if it's a big dom,
that's 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 upfront.
So the polite version, which is
what I'm doing here-- polite--
is to call or request
animation from them,
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
partway through a
frame, I'll wait
one frame for that to
take hold, an 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-- 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 the thing
is just-- 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,
and then I can ask for
offset width again,
and it would force
its [INAUDIBLE].
So I can force it into
the kind of reflow,
sort of that read-write
mutation, read-it 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 wait a frame with
a RequestAnimationFrame,
I know those tasks
will happen anyway
because they'll happen at the
end of the previous frame.
So 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--
say 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 to frame,
I basically go, if things
have changed in the interim,
just cancel.
Don't worry about setting
these next things.
And 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 intersperse
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, really?
Aw.
Also, at the same time,
it's not the worst code
I've ever written.
I've written some awful code.
Oh, well, we've all got
to move on, haven't we?
Right.
So that's taking the video
down to the small player.
It probably won't be a surprise
that you're pretty much 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
transformers because of that.
And then it's frame, frame,
frame-- the frame dance,
dun-dun-dum.
And then we are done.
So there you have it.
That's basically
it in a nutshell.
Then there's a lot of
boilerplate-y stuff
about making sure that
we can embed a link,
and 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 is 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, 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 popped the screen to the top,
because that's kind
of more what you'd
expect from the behavior
of it from the 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, doing your dance.
I don't think I am going to
set it as my main browser,
thank you.
Tempting.
Let's see if that works.
No, they've removed
clutter, so I can
focus on what I want to read.
Hey, 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.
L maybe I did
something different.
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 code is on GitHub.
As always, comments and
all the usual stuff below,
and I will see you next time.
