[MUSIC PLAYING] [APPLAUSE] MONICA DINCULESCU:
Hello, hello, hello. Actually, the late Paul
Lewis is now in the cloud doing machine learning. [BLOWS KISS] Pour one
out for Paul Lewis. So here I am. SURMA: Hello, Copenhagen.
So I saw this bit recently, and I really wanted
to try it on stage. So Bill Bailey did it. And what he did is, he
made the audience clap. But not clap like in applause,
but only exactly once. So put your hands together. Exactly once in sync with this. All right, ready? [AUDIENCE CLAPS] What an amazing sound. MONICA DINCULESCU:
[LAUGHS] Nailed it. [AUDIENCE CLAPS ON BEAT] SURMA: OK, enough time wasted. So hey, welcome. This is Supercharged,
which we usually do-- MONICA DINCULESCU:
Surma-charged. SURMA: Surma-charged. I'm rebranding. Because as you know, this person
is not the bald Paul Lewis, this is-- MONICA DINCULESCU: I'm
wearing a hat, though. This is the baldest
cap that I could find. SURMA: Close enough maybe? MONICA DINCULESCU:
Polymer colors. SURMA: Monica luckily
agreed to assist me today. And we're going to do the
Supercharged Live Live Live! as it is now,
I guess, tradition. But Paul did move
on to deep mines and is now working
basically on Google Home, and has been working on
the next edition, which is going to be the Google
Home Developer Edition. Or, as it's code
named, the Chrome Home. And so we have a
developer preview that we can hopefully show
off a little bit today while we do our code bits. So it should work pretty
similarly as it usually does. And yeah, just-- MONICA DINCULESCU:
Do you want me to do it because your
accent is kind of funny? SURMA: Yes, please. MONICA DINCULESCU:
[CLEARS THROAT] Hey, Chrome Home, what's the
weather in Copenhagen? CHROME HOME: Bleep. Wait. Really, I'm programed with
the knowledge of 50 Chrome developers. I know all about web
APIs and standards, and you're going to ask me
the same stupid shit you'd ask a regular Google Home? [LAUGHTER] MONICA DINCULESCU: Yes? CHROME HOME: [CLEARS THROAT] OK. The weather in Copenhagen
is kind of nice. For further details, please
consult your nearest window. [LAUGHTER] [APPLAUSE] SURMA: So-- MONICA DINCULESCU:
Thank you, Chrome Home. SURMA: We're going to maybe
make use of that later. MONICA DINCULESCU: We'll see. SURMA: Doesn't
seem that helpful. MONICA DINCULESCU:
Might as we'll have a third guest developer. SURMA: I guess Lewis has been
spending his time very well. Let's do the thing
we're actually here for. MONICA DINCULESCU: What is it? SURMA: We're going
to build something. MONICA DINCULESCU:
Sweet, what is it? SURMA: We are going to
build custom elements. Because after all, this
is the Polymer Summit, so I thought we should at
least be using custom elements. Although I'm actually
not using Polymer. MONICA DINCULESCU: Well, can't
win at everything, can we? SURMA: But because we're
focusing on the other thing that you say, we just
use the platforms. We're going to show you
the low level stuff. And I thought something that
I have been encountering or noticing lately is
that a lot of sites have, or blogs especially,
have images like these. I have some images of Copenhagen
on this test site here. And they load the
images right away. You load the page and all
the images are loaded. And that is actually
pretty hurtful because when you look at
it, you have 1.4 megabytes for opening a web page. And maybe you just want to read
first paragraph and then leave. And that's not cool. MONICA DINCULESCU: Mm-mm. SURMA: So I thought we
would build a lazy loading kind of custom element. MONICA DINCULESCU: I'm lazy. I'm into it. SURMA: Right? So-- MONICA DINCULESCU: I'm going
to stop you right there, Surma. SURMA: OK. MONICA DINCULESCU:
So I would like this. Paul Lewis told me not
to ruin his legacy, so we have to do some
housekeeping here. We would love
questions from you so that as Surma is
banging on the keyboard we can actually
answer your questions. Because who knows
what he's going to do? And because we don't have
comments on the YouTube stream, please tweet with the
hashtag Supercharged. I'll also be looking
at Polymer Summit, but I assume you're
just going to be tweeting about how awesome
we are on Polymer Summit. So hashtag Supercharged. Ask all of your questions and I
will answer them, or the Google Home. SURMA: Or forward them
to me to distract me. You know the usual deal. If you've seen this before,
you know how this works. But we don't have the
YouTube chat this time so we are using the twitters. Which also, not
walled off, -Surma. You should totally follow us. It's totally worth your time. MONICA DINCULESCU: But don't
tweet at us because I'm not reading my Twitter. Just Polymer Summit
and Supercharged. That's all I got. All right, Surms. SURMA: All right, let's go. MONICA DINCULESCU: Do it. SURMA: All right, so what I have
here is a pretty empty website, but we have four
images that I totally took myself in
Copenhagen. And we are going to try to make them-- MONICA DINCULESCU: So talented. SURMA: --lazy load. And just to show you what is
going on it, is super vanilla. It's literally four image
tags with four spacer divs in between, and
the styles are just-- the spacer is just
a very high div so that there's some
space in between. That is literally all we got. And everything else we're going
to write right here, right now live so you can actually
watch and ask questions. And stop me if I'm being stupid. So instead of using
the img tag, we are now going to turn
this into our SC image because branding is important. That's our Supercharged img tag. And to use those, we
actually have to, of course, define the element. So we're going to
include a new thing, which is called sc-img.js. And that is the-- MONICA DINCULESCU:
Make your text bigger. SURMA: Yeah? Like this? MONICA DINCULESCU:
Mm-hm, thank you. SURMA: All right, sc-img.js. And now the usual
dance where we go, OK, SCImg extends HTMLElement,
constructor, super, there we go. MONICA DINCULESCU: Wait,
but your constructor's not doing anything. Why is it there? SURMA: It's just--
have an element first. So now the images are gone
because the sc-image element is now in use. And we haven't done anything. So if we look at our
thing, HTMLElement, this should be saying
SCImg, and that is because I didn't actually
call custom elements defined. So my class will just
exist, but it wasn't used. So I'm going to go with
customElements.define('sc img', SCImg) I think? Is that how it works? MONICA DINCULESCU: Yeah. SURMA: We shouldn't
still see anything. But now it says SCImg,
and now we are actually having our own custom
elements in place, and now we can start
working with them. So the first thing I
want to do is make-- MONICA DINCULESCU: Typos. SURMA: --custom elements by
default. Our display inline. And for images we
don't really want that. We want them to be blocks, nice
and wide and fill the space. So we're going to use Shadow
DOM to give this element some inherent styles. MONICA DINCULESCU: You should
make the text even bigger. SURMA: Even bigger? MONICA DINCULESCU:
Also somebody asked. He's using VS Code, I believe. SURMA: It is VS Code. So let's create-- oh,
this is going to be tough. I'm going to close the sidebar. Let's create a template. And the template
gets some innerHTML, and I'm using some
template tags. And in here we have the style. And the reason I'm
using a template is because instead of-- in every constructor
call, I could just be setting innerHTML,
but that always starts the parser, which
I don't really want. So I'm going use
a template which is much quicker to instantiate. So in here I'm going
to say display: block because our element is
supposed to be display: block. In our constructor, I'm going
to say attachShadow({mode: 'open'}). MONICA DINCULESCU: Going
to answer a question from the livestream. So he didn't actually need
to write the constructor. He wasn't going to add anything. But I think he
knew ahead of time he was going to
add other things. If you're only
calling super, you don't need to define
the constructor there. SURMA: That is true. I do it anyway. It's just like muscle memory. MONICA DINCULESCU:
Oh, just like typing. SURMA: Because sometimes you
want to add stuff, and then-- so now we can do
template.content.cloneNode-- [LAUGHTER] true. MONICA DINCULESCU: Wowza. SURMA: This is a family show. [LAUGHTER] So let's close the
console, and hopefully-- we should say display blocks
[INAUDIBLE] elements are not display blocks. There's still no image. MONICA DINCULESCU:
Cool, that was our demo. Thank you for coming. We're done here. SURMA: That's it. Not quite. So let's see how we
can load the image. Again, what I wanted
to do is to load them when they come into view. And there's a kind
of new primitive on the web which is called
IntersectionObserver which allows you to-- why don't you explain it? MONICA DINCULESCU:
IntersectionObserver is a thingamajig
so that when you scroll a thingamajig
into the view, the IntersectionObserver
says, hey, the thingamajig
is into the view. You should do
something about it. SURMA: That was super concise. MONICA DINCULESCU:
And it's really useful if you have a
giant block of text and an image at the
bottom, and you really don't want to load that image
until that image is actually in the view because. Maybe it's never going
to get in the view. So when it comes
into the view, you're like, bam, show that thing. SURMA: All right. MONICA DINCULESCU: Hey,
I've got a question for you. SURMA: All right. MONICA DINCULESCU: Why is your
template outside of the class? SURMA: Because I didn't want
to recreate the template for every instance. It's just there for me to reuse. So it's going to be parsed once,
and I can re-instantiate it every time when a
new element is being created, which is super fast. MONICA DINCULESCU: Cool. SURMA: I mean, unless you're--
if you're creating a million images, this is actually
going to make a difference. Most of the time it won't. But this is just a good pattern
to adopt so you don't run into these kind of problems. OK, IntersectionObserver. Let's talk about this. You create an
IntersectionObserver , and you give
IntersectionObserver a callback. And this callback is
going to be called every time some of your
elements change their state. And their state meaning
being inside the viewport or outside the viewport. MONICA DINCULESCU: When they
intersect with the viewport. SURMA: Yeah, exactly, exactly. MONICA DINCULESCU: Hey,
hey, hey, Chrome Home, how do you say intersect
with the viewport in German? CHROME HOME: What? Sorry, the Surma Module
has not yet been installed. MONICA DINCULESCU: Google it. CHROME HOME: Um. [LAUGHTER] Intersector das ein
Viewporten, s'il vous plait? [LAUGHTER] SURMA: Did you mean, hilfe,
meine Badewanner brennt? CHROME HOME: Yeah. Yeah, that's what I meant. SURMA: Well, that means,
help, my bathtub is on fire. [LAUGHTER] CHROME HOME: Sorry, Google
Play services has stopped. [LAUGHTER] MONICA DINCULESCU: God, it's
not working really well. SURMA: I'm not
impressed with Paul's MONICA DINCULESCU: No. SURMA: --work recently. MONICA DINCULESCU: He's been
gone for like, two months. What's he been doing? SURMA: Downhill. So the callback. It's a callback
that, as a parameter, gets a number of entries. And each entry is for
different elements and how the state changed. So we're going to go
through all these entries. And if that entry is
intersecting, meaning it is currently inside the
viewport on the element, which is the entry.target. We are going to set
an attribute which is going to be called full,
which is meaning like, it should now show the
full version of the image. And that is pretty
much all we're going to use
IntersectionObserver for. Whenever an element
scrolls in the viewport, the attribute full
is going to get set, and then we can
react to that change with our standard
observed attributes that we know from
the custom elements. So for that, we need our
static get observedAttributes, which is going to be
the full attribute only. And since we only have that
one observed attribute, our attributeChanged callback
doesn't need any parameters because we know it's just
going to be the full attribute. And what we're going
to do is, if it's already full you're going
to return because we're not going to load the image twice. Once is enough. And otherwise, we're going
to create a new element. createElement('img'). Image source is going
to be this source. And now I just realized I forgot
that I should get some getters. So we are using this.full. MONICA DINCULESCU: While
you're doing the getters, lovely question
from the audience. Which browsers support
IntersectionObserver? And since I'm too lazy
to Google it, hey, Chrome Home, which browsers support
IntersectionObserver? CHROME HOME: Uh. [KEYBOARD CLACKING] It's Edge
15, Firefox 55, Chrome 58, Opera 46, and
Samsung Internet 5. SURMA: That's actually
pretty decent. MONICA DINCULESCU: Yeah. SURMA: So it's something. There is a polyfill, I think. There is a good polyfill for it. MONICA DINCULESCU:
That you wrote. SURMA: What? MONICA DINCULESCU:
Didn't you write it? Somebody else wrote it. SURMA: I did the
first version, then I passed it on to other people
who were much smarter than me. MONICA DINCULESCU: Nice SURMA: But you don't
need it apparently because that was actually a
pretty decent support list. So most of the time you'll
be running without it. MONICA DINCULESCU: So you're
doing two getters and setters for source and for full. SURMA: I'm not doing setters
because I'm not going to-- MONICA DINCULESCU:
Don't set anything. SURMA: Yeah, pretty much. So we're creating a new image. We are copying the
source from our image to the actual image tag. And then-- MONICA DINCULESCU: And
the reason why doing that is because the moment you
set a source on an image, it's going to start loading. You can't stop it from
learning on that image no matter how hard you try. SURMA: Exactly. And now we're going to wait
for it to finish loading. MONICA DINCULESCU: Train
don't stop in that platform. SURMA: And once it's loaded,
we are going to shadowRoot, attach it to the image. That is actually not-- MONICA DINCULESCU:
That's not right. SURMA: That's
appendChild, right? MONICA DINCULESCU: Nice. SURMA: All right, this
looks pretty OK, I think. Let's give it a try. I'm sure I did something wrong. Where's my console? Nothing is happening,
which makes sense because I just created
the IntersectionObserver, I didn't use it anywhere, which,
you know, might be helpful. So on connectedCallback, this
is the second part of the API. So the IntersectionObserver you
create, you pass a callback in to know which code to execute
when something changes. And then you have to tell
the IntersectionObserver what to actually observe. So I'm going to call
observe('this') this because we're going to
observe the elements itself. And because we are good
citizens of the web, we are also going to do our
disconnectedCallback and call unobserve. MONICA DINCULESCU: Nice. SURMA: So now we
are getting errors line 13, which is
totally what I expected. Oh, setAttribute. I always disliked this. I only want to
set the attribute, and yet I still have to say
set it to an empty value. MONICA DINCULESCU:
Or true, yeah. SURMA: Or true, I guess. MONICA DINCULESCU:
Whatever you want. SURMA: But it just
seems unnecessary. So let's do this. MONICA DINCULESCU: Backwards
compatibility for you. SURMA: I guess
nothing is happening. Oh, because we are setting
full and they we're checking full, which we
just set and therefore this is going to abort. This is not smart. So I'm going to
just call it loaded. So we're going to have
two properties now, the full attribute
and the loaded. Full is when it's
supposed to be on screen, and loaded when it actually
is loaded and on screen. So in this callback,
when it's loaded, we are actually going to
call this loaded as true. I'm not going to even define an
attribute because this is live MONICA DINCULESCU:
Mmm, it just does. SURMA: --and it's going
to work anyway, right? So let's see. Cool. I mean, it's a little
bit big, so that's not-- but the image is in here and
the Shadow DOM, so let's do it. MONICA DINCULESCU: To be fair,
you didn't set any styles. So it's going to
be as big as it is. SURMA: Yeah, let's
change that, shall we? So our image here-- and this
is what I love about the Shadow DOM, I don't have to do complex
selectors because it is scoped by the Shadow DOM anyway. So I can just go image, and say
width 100% and be done with it. Boom. And now the thing is, if
you the network panel, only Image A has been
loaded with 265 kilobytes. Once I scroll down-- MONICA DINCULESCU: Yes. SURMA: --the second one loads. Scroll down, third one. And this is, in terms of data
conservation for the user, this is much better
because now they only actually download the data
they actually have on screen. MONICA DINCULESCU: So does
the IntersectionObserver run on every pixel scroll? How does it actually work? Is it performant, at least? SURMA: It is super performant. So I think, as far as I know,
if you don't use the polyfill but have a native implementation
of IntersectionObserver, it hooks into the
actual layout engine. So the browser is
obviously the only entity that knows if something
is on screen or not. And once it is on
screen, it queues up one of these
callback invocations. And those are
dispatched in idle time, and that means you will
only get to process these entries if the
browser has time to do so. So if you're busy encoding
a GIF or whatever people do these days on the main thread,
your IntersectionObserver callback will be delayed
until there is actually head room to do these kind of-- MONICA DINCULESCU: So
you're not blocking layout, your blocking
panes, you're not blocking your animations. SURMA: Or scroll. It's great. So it can basically
only get better because the most
important thing, really, should be to be
interactive for your user. MONICA DINCULESCU: Cool. SURMA: All right. MONICA DINCULESCU: I'm going
to answer some more questions. Is that OK? Is how we do it? Is that-- SURMA: You're doing
pretty good, actually. MONICA DINCULESCU: So
one of the questions is, if the image content
is the Shadow DOM, can robots access it? So the search bots. And they can if
they run JavaScript. So this is the same
question for SEO-- SURMA: Yeah, pretty much. MONICA DINCULESCU:
--that you saw. Surma-- what does SEO stand for? SURMA: Search
Engine Optimization. MONICA DINCULESCU: Surma
Engine Optimization. SURMA: Surma Engine
Optimization, I like it. MONICA DINCULESCU: Perfect. Yeah, so as you saw, exactly the
same answer as in the SEO talk are from here. If you're running JavaScript,
it will be accessible on there. SURMA: It should be fine. MONICA DINCULESCU: And also, you
should probably set some alt. We're not doing this
because it's not live, it's not production. SURMA: Oh, I should be
setting alt, but I-- MONICA DINCULESCU:
But that image doesn't have an alt. Rob Dodson
is it probably in the audience and he's not impressed. SURMA: He's going to punch me. MONICA DINCULESCU: Yeah. Please don't punch us. SURMA: Because I'm
on his how-to team and I didn't do
the accessible bit. MONICA DINCULESCU: Yeah. SURMA: I'm actually bad. OK, I mean, that is pretty cool. This is working. We could say we're done,
but there's something else. If you scroll down and reload
the page and scroll up, the image just kind
of pop into existence because at the start,
our image has no height. And then once the
zero height diff comes into view, the
image, suddenly it has a height so it kind of
appear, which is really sad. So what I would like
to do-- and this is something where this
image is going to be better than the native image element. It's going to have
support for aspect ratios. MONICA DINCULESCU: Nice. SURMA: So we're going to-- I mean, you can do it
with a negative element. MONICA DINCULESCU: You're like,
preemptive three questions from Twitter. You're nailing it. SURMA: Bam, impact. All right, so what
we're going to do is, we're going to write
a tiny bit of a back end. So I'm going to bring
the sidebar back real quick to create a new
file, which is going to be-- no, not in here. Down here. There. I'm going to write
a little back end. And we're going to
be using some express because whenever I
do Nodes I just use express because it's easy. And we're going to kill
our Python web server and instead start
our new server. MONICA DINCULESCU: Are you going
to do some Surmasite rendering, you would say? SURMA: It's so going to
be Surmasite rendering. MONICA DINCULESCU: Nice. I'm here all day. SURMA: So we create a new app. And that app-- hang on. That app uses the
Express.static middleware because mostly we're just going
to do static page delivery. And app.listen on 8080. And so now everything
should be working the same. Cool, it's still loading. Now we're using
our new back end. And now we're going
to do something new because we're going to define
our own route for HTML files. Request, response. And what I want to do
in your is basically inspect our images to figure
out what their aspect ratio is, and do some CSS hackery to give
the elements an aspect ratio so that they retain their
aspect ratio even if the image data has not been loaded yet. And to do that, we have to
first figure out which file is actually being loaded. So head the file path,
which is req.url. And if the file path
ends with a slash, that usually means that we have
to add index.html, right? Amazing. Because it ends
with a slash, it's a folder, and that kind of deal. And now we have to read
the file, basically. And to do that, you have
to do the fs module. I'm going to use-- I'm on Node 8, so I can use
all the new, shiny stuff. So I can use the new promisify
function from the utils module to turn the old callback
version of file system into promise version. And we all like promises,
so I'm going to do that. MONICA DINCULESCU:
Hey, hey, Google Home, do you like promises? Oh, Chrome Home, sorry. Do you like promises? CHROME HOME: Yeah. I prefer streams, though. MONICA DINCULESCU:
[LAUGHS] Nerd. SURMA: All right, so
it's pretty simple. You just pass on the function
that has the standard Node callback layout. The last parameter is a
callback with error end result. And it turns into something
that now is a function that returns a promise. And that means that we
turn this whole thing into an async function,
and can now do const. So if you're going
to read the buffer, we can do, await,
readFile, file path. This is not going to work
because we have to add static. Then we can turn it into a
string so we can send it back. And then we can send
these contents back. Let's hope that works. Still working, cool. So now we can read
files in a way which I think is much nicer to read
than having either promises or callbacks, honestly. async, await really makes
this code much easier. All right, we have the
buffer, we have the content. And now we're going to do
some post-processing on it because we need to figure out
which images are being loaded, load all these images,
and then figure out what their aspect ratio is. So we are going to do, and I
know you're going to love this, we're going to some regex magic. MONICA DINCULESCU: Oh, God. SURMA: So what some people
don't know, split actually-- MONICA DINCULESCU:
For the record, I haven't seen this code before. So I'm getting
anxiety every time he says these words, like we're
going to do some regex magic. SURMA: Do you want
to write the regex? MONICA DINCULESCU: No. SURMA: No, OK. MONICA DINCULESCU: Going to
answer some questions though while you type your regex. SURMA: Go for it. MONICA DINCULESCU:
Because nobody needs to know how you're doing it. SURMA: No, I'm going
to explain the regex. So go-- MONICA DINCULESCU: Oh. SURMA: --for the questions,
and then I'll do the regex. MONICA DINCULESCU:
One of the questions is, why aren't we extending the
image element, with is equals? SURMA: Because
that's not a thing. MONICA DINCULESCU: That is
not a thing, unfortunately. So is equals is one of the
battles we lost a little bit for a custom elements. And it's not-- SURMA: I mean, in the spec. MONICA DINCULESCU:
It's in the spec, but it's not actually
implemented everywhere. Not even Chrome, I think,
has it for the v1 spec. SURMA: No, we don't. MONICA DINCULESCU: So it
doesn't actually work anywhere. The polyfills don't have it. So you can use is
equals all you want. It's not going to do
anything right now. SURMA: Some browsers
have expressed very strong dislike
of the is pattern. And if one browser
doesn't do it, there's no point so
far at least in just doing it in some browsers. Because it's also
very hard to polyfill. I don't even think it's
polyfillable at all. Not sure about that. But-- MONICA DINCULESCU: It's-- SURMA: --for now we have
to live without subtyping native elements. We can only do HTMLElement
and nothing else. MONICA DINCULESCU: Mm-hm. Carry on, let's do this regex. SURMA: So what I'm going to do,
we're going to write a regex, and we're going to find
all the sc-image elements. And we're going to do this. So we want to have everything
until the closing tag. Come on. And see image. It's beautiful, isn't it? And let's keep it this way,
and let's call join down here. And let's work on
the source for a bit so we can see what is going on. This means it is not working. So that's good. We're splitting this. AUDIENCE: [INAUDIBLE] SURMA: That is correct,
thank you very much. And my image has disappeared,
which is actually true because I need to put
parentheses around this. So now this should
look the same. MONICA DINCULESCU: Nice. SURMA: The good part is that
now newContent is an array. And it's either going
to be remainder code or it's going to be just one
isolated sc-image element. Just to show what
I'm talking about, I'm going to console.log
newContent for a bit. Going to refresh. And then in the
console, it's an array, and every second element
now is an sc-image element because that is the part
that matches the regex. And everything
that doesn't match is going to be put
in another element. So we now just split
apart our entire document into what is an sc-image
and what is not an sc-image. And now we can do
post-processing on that. So we're going to
do next is, we're going to remove the semicolon
and we're going to map. And each of these items is-- if the item starts with
sc-image-- actually, when it doesn't
start with sc-image, don't want to do anything
because we don't care about it. So we're just
going to return it. And otherwise, we want to figure
out what the actual source attribute is. So again, we're going
to do regex because-- MONICA DINCULESCU:
Oh my goodness. SURMA: --that's how I roll. [MAKES NOISE]
There's parentheses around this and
that, and then we're going to do an exec on the item. And then we're going
to return something. Let me think for a bit. Let me just do a test. I'm going to return the
source just to see it works. So now we know the
source actually has only the value of the source
attribute, so that's good. MONICA DINCULESCU: It's
probably got to read that fine. SURMA: And now we're
going to do the item. Actually, no. The item can actually stay. What we're going
to do now next is, we're going to
actually figure out what the aspect ratio
of the image is. And this is where it gets-- we go into a look of the
weeds of the Node ecosystem because now we have to look
into image processing libraries. I just googled a bit and
took the first one I found, which is called Graphics Magic. For short, gm. And we can subclass
it to use imageMagick because that's the only one
I've installed on my system. Don't worry too much about it. Basically all the image
processing libraries can do what I want, resize
images and figure out what the size of an image is. And what I want to do down here,
I'm going to load the image. And that's fairly easy with this
library So I'm just going to do './static/' + src. And let's just do a console
lock to see if that worked. I hope it will. So this should
all look the same. Looking good. We have loaded an image. Size function. Now this is where
things are a little bit weird because the library, as
all Node libraries are a little bit old and have callbacks. But the cool thing is,
promisify, the function I loaded from Node 8 actually
works on librarires as well. As long as you've conformed to
the standard callback pattern that Node has, this
is going to work. So I'm going to call
img.size.bind(img). So I pass on the function. It's going to turn that
function into a promise version. And then I can do width,
height, await sizeFunc. MONICA DINCULESCU: Why are
you doing all this awaiting? Why don't you do it sync? SURMA: Because the
library is not synced. MONICA DINCULESCU: There. SURMA: That's how
callbacks work. So this is going to-- ooh. MONICA DINCULESCU: If you
would've done it sync, it would have been fine. No, I'm kidding. SURMA: I'm using await inside
the map callback, which is on an async function. So I'm just going to make
it an async function. And for that to
work, I also have to do a promise.all because
now all the array elements are going to be promise values. There we go. So this should work again. Cool, cool. So we see we have
four images on a page, and we have four
widths and heights. Magic. That's why called imageMagick. MONICA DINCULESCU: Image magic. SURMA: Because you just do some
code invocation, at some point you get what you actually want. Now we talk about something
that I really like, the-- MONICA DINCULESCU: Animations? SURMA: No. MONICA DINCULESCU: Are you
going to do some animations? SURMA: No, not yet. I'm going to talk about the
aspect ratio hack in CSS. Oh, we can ask Google. We can ask. We can ask. MONICA DINCULESCU: Go for it. Maybe he'll understand you. SURMA: [CLEARS THROAT]
Accent down. Hey, Chrome Home. CHROME HOME: Bleep. SURMA: How does the aspect
ratio hack in CSS work? CHROME HOME: Server, I hate you. So you have an element, and it
has another element or pseudo element inside it that has a
padding top that is the aspect ratio that you want. And then inside it, you can
use absolute positioning to keep something the same size. SURMA: Everybody
totally got that. That was well-explained. I'm hoping the code will now
actually show how this works. So the weird thing is, when
you define a padding top in percentages, so
padding top 50%, that 50% is not the height
but the width of the parent element. Don't ask me why, but
that's how it works. And the cool thing about that
is that we can say here-- actually, I should
be using a temp tag. We can abuse this basically
to define an aspect ratio. Because we're going
to do is, we're going to do height divided by
width times 100 in percent. And that means the
wider the image is, it will grow in height as
well because padding top is proportional to the
width, not of the height. MONICA DINCULESCU: This hack
is also really good for iframes whenever you're
loading a YouTube video that you are importing
and it's always a weird aspect ratio. Do this. Do this for everything. SURMA: So just to show that
this is actually working, I'm replacing the closing
characters of the elements to inject some styles,
which are actually-- MONICA DINCULESCU:
And the closing, yeah. SURMA: Yes. Like this. Let's take a look at this. OK, so you can see we
have injected percentages successfully. So let's look at the
actual visual version. Not quite what I was going
for, but we can probably fix it with some styles. So we're going to do
position: relative. And so the problem
right now is that we have a padding on
these elements, and the contents
of the [INAUDIBLE] are being pushed down by the
padding, which is not one. So we're going to just
absolutely position the Shadow DOM image inside at the
top and at the left so it doesn't really care. What? Oh, I think we loaded too fast. There we go. And because we
can't really see it, I'm going to slow down
the network, which-- where is it? Is it down here somewhere? There it is, network conditions. So let's do it on slow-- Fast 3G I think
should be good enough. So you can see the
rectangle is there even though the image is not loaded. And once it's there, it just
replaces the red rectangle, which is still underneath
there, technically. But now we have
images that consume the space the image will
need once it's loaded. And that's something the
native image tag doesn't do. MONICA DINCULESCU: Does not. SURMA: I mean, you can use the
same tag on the native image element. You can just defined
a padding top, but I thought that this was a
really neat trick to show off. MONICA DINCULESCU:
And this is how you don't have your
stuff just jumping around whenever images come in. SURMA: That the best part. If someone loads the
page at the bottom for some reason and
scrolls upwards, stuff isn't going to jump around
because the images already allocate the space
that they need. The only thing I dislike
about this is that they kind of like-- the red
square is not really-- MONICA DINCULESCU:
It's annoying, yeah. SURMA: Yeah. MONICA DINCULESCU: I'm going
to steal a question, though, first. SURMA: Go for it. MONICA DINCULESCU:
The question was, what does the host selector does? SURMA: Oh, that's
a good question. MONICA DINCULESCU: And that is-- I'm going to take that one. SURMA: Go for it. MONICA DINCULESCU: It basically
lets you style the custom element itself. So if you think
a custom element, it has basically its
shell of a custom element. And host is that element itself,
not the things inside of it. SURMA: And something you
can't do, fair warning, is if I had written it
like-- oh, actually I should have put it down here. Something like this wouldn't
work I think because you cannot really-- what, this has worked? MONICA DINCULESCU: That
definitely works, yes. SURMA: So it's a
function, I guess. MONICA DINCULESCU: Yeah. SURMA: Not a-- MONICA DINCULESCU:
It's like a pseudo-- SURMA: Not a measure
in the classical sense. So that's something
to look out for. It also can't go down
multiple children, I think. Only top level children. It's a little bit iffy. But if we have
good documentation on DevelopersAtGoogle.com/web,
which you should totally go to and read up on this. And yeah, as I said, these red
squares are a little bit sad. So what I'm going to do instead,
I thought we could also do-- MONICA DINCULESCU: An animation? SURMA: No, not yet. But maybe later. Maybe I'll humor you. MONICA DINCULESCU: You're
not a one trick pony. SURMA: I thought I would do
the medium bit where they have the blurred version of the-- MONICA DINCULESCU: Like a
low-res base64 image background from Twitter's suggestion? Do you think we should do
this Twitter suggestion? SURMA: Oh, someone has
been thinking along. I like it. That is exactly what
I'm going to do. So we are going to
generate a thumb. And because as I said,
the library is async, we have to create a new
promisify functional scans. So I'm going to call
resize.toBuffer.bind(img) because otherwise
it doesn't work. And then our thumb
will be the thumbFunc. MONICA DINCULESCU: How do
you say thumb in German? SURMA: Chrome Home? MONICA DINCULESCU:
Hey, Chrome Home, how do you say thumb in German? CHROME HOME: Ein Thumb. [LAUGHTER] SURMA: It's actually Dauman,
but that's all right. So I thought I would do a thumb
size because we can probably play around with the
resolution a little bit. So I'm just going
to put it here. Thumb size is going to go with
8 because that seems reasonable. thumb is now the image buffer. Actually, that's not true. Or is it? That is something
that needs to go here, if I remember correctly. And here we can just say png. There we go. MONICA DINCULESCU: Do you
have all your brackets? Do you need an extra bracket? SURMA: I think so,
I think I'm good. MONICA DINCULESCU: OK. SURMA: It's not complaining. And so our thumbURL, what
we're going to do is, we're going to encode the thumb
version, as you already hinted at, as a base64 inline URL. Because we don't want
to wait for the network to load a low-res
version so we can then show the higher-res version. So we're just going to put it
inline into the document right away so when the
HTML arrives we have something on screen,
which I think is a much better experience. So the thumb
[INTERPOSING VOICES] MONICA DINCULESCU:
Somebody on Twitter wants me to do the
animation stuff. You guys, I don't know
how to animate anything. Like, a transform
is too hard for me. I am really shit at CSS. SURMA: We're not going to do
much transform today, I think. MONICA DINCULESCU: Yeah, I know. SURMA: toString. And luckily Nodes in
contrast to the web has just two base64, which
is really convenient. MONICA DINCULESCU: Nice. SURMA: And what we're
going to do here is, we're going to do,
say, our background URL. Background image is a URL. And in here we're
going to do thumbURL. Boom. This looks about right, I think. I'm still on the slow
network so we can actually see the loading pattern. Or, I'm actually did a mistake. I probably did a mistake.
thumbURL is not defined. Why not? [MAKES NOISE] But
it's right here. AUDIENCE: [INAUDIBLE]
thumbURL [INAUDIBLE] SURMA: Oh, that is-- it should just be thumb. MONICA DINCULESCU: Thumb. SURMA: Thank you. MONICA DINCULESCU:
A zahenhoggle. Zay-- Sahenoggle? SURMA: That wasn't quite
what I was hoping for. I think it's actually correct
because it's just an 8 by 8 image tiled
all over the place. But that's not the visual
we were looking for. MONICA DINCULESCU: Got
to stretch that out. SURMA: Got to stretch it out. So what I'm going to do,
in the inherent stats I'm going to say background
size is 100% 100%. MONICA DINCULESCU: [GASPS] SURMA: Cool. MONICA DINCULESCU: Yes. SURMA: Cool. MONICA DINCULESCU: Yes. SURMA: So we're on fast speed. And this is pretty good. But now, now your moment. What could we do next? MONICA DINCULESCU: I'm going to
answer a question from Twitter. SURMA: OK. MONICA DINCULESCU:
One of the questions was, why didn't
you just distribute an image as a slot in here? And that would be
annoying, wouldn't it? For every image that you
wanted on your screen? SURMA: Yeah. Also, wouldn't be
framework compatible because then whenever you
have something like V DOM, it will just eliminate the
image out of my [INAUDIBLE].. In general, it is
rarely advisable to just sprout new children into
a custom element dynamically. MONICA DINCULESCU: No, but you
could have had the sc-image, and then you would also put
you as the page author, put your image in there. That's just annoying. You're writing the image twice. SURMA: Yeah, no. I like this better. MONICA DINCULESCU: Yeah,
just do it as a child. SURMA: To be fair, that
would work as well. MONICA DINCULESCU:
A Shadow child. Yeah. SURMA: I feel like it
would be more work. MONICA DINCULESCU: I would
probably copy-paste it twice. It would be like sc-image,
and then an image, and then an sc-image,
and then an image. SURMA: OK, so I'm
going to ask you again, what could we do next? MONICA DINCULESCU: Put on
your hat and get a clap out of people? SURMA: Yes. MONICA DINCULESCU: I genuinely
don't know the answer to this question. SURMA: Because I think people
might not be quite awake. [AUDIENCE CLAPS] It still works. [AUDIENCE CLAPS ON BEAT] MONICA DINCULESCU: Trick them. SURMA: All right. MONICA DINCULESCU: OK,
so I'm going to ask you, what are we going to do next? SURMA: But this is your moment. You can do some animation. MONICA DINCULESCU: We
can do some animations. SURMA: And actually,
it's going to be super, super easy because I-- the thing that annoys
me a little bit is, you have this
nice, blurred version and it's going to be like, pew. That could be a
little bit nicer. MONICA DINCULESCU: Are you
can do a transform over there? SURMA: No. MONICA DINCULESCU: Dammit. SURMA: You're wrong. So what I'm going to do instead
is, we are just going to-- MONICA DINCULESCU:
Guys, there's a fight on Twitter about how to properly
translate thumb and thumbnail, for the record. SURMA: Ooh. MONICA DINCULESCU: Somebody-- SURMA: I'm going to
get in on that later. MONICA DINCULESCU: Yeah, yeah. SURMA: So I'm going to define
a keyframe animation which goes from opacity 0, and that's it. MONICA DINCULESCU: [GASPS] SURMA: Because it's black. MONICA DINCULESCU:
What does it go to? SURMA: Nobody knows. MONICA DINCULESCU: Ah. SURMA: So I'm going to
put this on the image. And the nice thing about this is
that this way the browser will know it's an animation. It will do the whole promotion
to its own layer and make it fast, and na-na-na-na-na. MONICA DINCULESCU: And if
you're writing production code, you'll put all the other
vendor prefixes for keyframes and all that jazz. SURMA: Well, you have
tools for that, right? I didn't-- MONICA DINCULESCU: Cool. SURMA: --write those by hand. But-- MONICA DINCULESCU: I
do, like an animal. SURMA: We have forgotten
the most important thing about Supercharged. This is not
production-ready code. MONICA DINCULESCU:
Don't copy it. SURMA: We never do
production-ready code at Supercharged. This is about concepts and
things we can do on the web. But you shouldn't be
copy-pasting this. We didn't do accessibility. MONICA DINCULESCU: We
did not do accessibility. SURMA: I didn't reflect on
my properties correctly. MONICA DINCULESCU: Mm-mm. SURMA: I only have
getters and no setters, which is also not nice. We're not even
handling if you change the source on an element. MONICA DINCULESCU: Where if
you're scroll really fast and you creating children a lot. SURMA: Yeah, that's-- so I
wanted to show you how it can be acceptably easy to
load dynamic images lazy, and have a nice
transition on it, not to have an element that
you can use everywhere. But still, I think the
concept is pretty interesting. So the last thing
we're going to do is, I'm going to put an
animation duration of five seconds on it,
mostly because I want to see the thing happening,
not because five seconds is a good value here. But this should be
enough to actually have the image just fade in. Because by default, the
opacity default value is 1. So this way I
could leave out the to because it's going to
transition to the default value. And secondly, animations
don't loop by default. So I don't have to worry about
the fade going over and over. MONICA DINCULESCU:
Over and over. SURMA: It's just going to
fade to the end position and stop there. So hopefully we're going
to see a blurred image, it's going to load, and then
it's going to fade in, right? And we scroll down. We can actually do
the throttling now because we don't want
to wait that long. We see the next image,
and it's going to fade in. And that, I think-- MONICA DINCULESCU: Wow. SURMA: --makes a much nicer
experience, doesn't it? MONICA DINCULESCU:
That's wonderful. SURMA: And just because we
have a couple of minutes left, I'm just going to
show you one thing. If you're more into
the pixillated look, one property is all it takes. And I'm wrong. MONICA DINCULESCU:
[INAUDIBLE],, what is it? [LAUGHTER] SURMA: There you go. And that's something when
you increase the resolution a little bit on the thumbnails. Let's go to 16 by
16 because why not. You can actually see
the patterns already emerging a little bit, which
also can be a really nice look. And I think I'm
going to stop here. I'm going to push this code
up on GitHub, as we always do. It's on
GitHub.com/GoogleChrome/ui element samples. But we're also going to
put it in the description on the video. MONICA DINCULESCU: Yeah. SURMA: Thanks, everyone,
so much for watching this, for bearing with me through
all the weird phases of this. MONICA DINCULESCU:
It's beautiful. SURMA: Thank you for
handling the Twitter and confusing the
heck out of me, and making me use
the head twice. Thank you for clapping along. MONICA DINCULESCU: Could
have been third times. SURMA: And if you
have any questions, ask me on Twitter later. Or I'll be around
a little bit more. Thank you. [APPLAUSE] [MUSIC PLAYING]