José M. Pérez
Solutions Engineer at Facebook
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
Version A
JSDayES
Madrid
2017
Speakers & Schedule
0.0s
0.3s
0.6s
0.9s
1.2s
Version B
JSDayES
Madrid
2017
Speakers & Schedule
0.0s
0.3s
0.6s
0.9s
1.2s
JSDayES
Madrid
2017
Speakers & Schedule
JSDayES
Madrid
2017
JSDayES
Madrid
2017
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
end = end time in milliseconds
VC = % visually complete
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
can sometimes lead to
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
images
1712kB (66%)
scripts
426kB (16%)
other
464kB (18%)
source: http archive 15 apr 2017
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
TIP 1
PROGRESSIVE IMAGE RENDERING
@jmperezperez
gif | png | jpg | webp | <the_next_thing>
PROGRESSIVE IMAGE RENDERING
@jmperezperez
<picture>
<source type="image/webp" srcset="2700x1209/my-image.webp 2700w,
1024x1024/my-image.webp 1024w,
600x600/my-image.webp 600w"
sizes="100vw" />
<source srcset="2700x1209/my-image.jpg 2700w,
1024x1024/my-image.jpg 1024w,
600x600/my-image.jpg 600w"
sizes="100vw" />
<img class="rsImg" src="600x600/my-image.jpg" alt="My beautiful image" />
</picture>
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
1 column
2 columns
3 columns
TIP 2
<img sizes="(max-width: 30em) 100vw,
(max-width: 50em) 50vw,
calc(33vw - 100px)"
srcset="profile-200.jpg 200w,
profile-400.jpg 400w,
profile-800.jpg 800w,
profile-1600.jpg 1600w"
src="profile-400.jpg" alt="Pinchito">
PROGRESSIVE IMAGE RENDERING
@jmperezperez
Challenge: Keeping in sync markup and CSS
RESPONSIVE IMAGES
PROGRESSIVE IMAGE RENDERING
@jmperezperez
TIP 3
&
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
// load image when it's within 100px of the viewport
const options = {
rootMargin: '100px'
}
const callback = entries => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
// load image
}
});
};
const observer = new IntersectionObserver(callback, options);
observer.observe(document.querySelector('.lazy-img'));
PROGRESSIVE IMAGE RENDERING
@jmperezperez
INTERSECTION OBSERVER
class LazyImage extends React.Component {
constructor() {
this.observer = new IntersectionObserver(entries => {
if (entries[0].intersectionRatio > 0) {
// load!
}
});
this.element = null; /* render() will set it through a ref */
}
componentDidMount() {
this.observer.observe(this.element);
}
componentWillUnmount() {
this.observer.unobserve(this.element);
}
...
}
PROGRESSIVE IMAGE RENDERING
@jmperezperez
INTERSECTION OBSERVER
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
PLACEHOLDERS
Nothing
Placeholder
Solid colour
Progressive image loading or "Blur-up"
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
<figure>
<div>
<div/> <!-- this div keeps the aspect ratio so
the placeholder doesn't collapse -->
<img/> <!-- this is a tiny image with a small
resolution (e.g. ~27x17) and low quality -->
<canvas/> <!-- takes the above image and applies a blur filter -->
<img/> <!-- the large image to be displayed -->
<noscript/> <!-- fallback for no JS -->
</div>
</figure>
PROGRESSIVE IMAGE RENDERING
@jmperezperez
Baseline
Progressive
PROGRESSIVE IMAGE RENDERING
@jmperezperez
With the Progressive JPEG method [...] cognitive fluency is inhibited and the brain has to work slightly harder to make sense of what is being displayed.
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
Unfortunately, the standard JPEG header is hundreds of bytes in size. In fact, the JPEG header alone is several times bigger than our entire 200-byte budget. However, excluding the JPEG header, the encoded data payload itself was approaching our 200 bytes.
PROGRESSIVE IMAGE RENDERING
@jmperezperez
Header (Quantization Table and Huffman Table) | Compressed Data |
---|
Client (mobile app) GraphQL
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
Canny Edge Detector
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
<svg>
<polyline points="51,1 61,1 61,2 56,4 56,3"/>
<polyline points="52,1 50,2 51,3 50,4 50,9 46,10 46,8 48,8 48,9"/>
<polyline points="61,4 61,5 58,6"/>
...
<polyline points="62,58 61,59 61,60 50,62 50,61 51,61"/>
</svg>
PROGRESSIVE IMAGE RENDERING
@jmperezperez
Just because you can it doesn't mean you should
PROGRESSIVE IMAGE RENDERING
@jmperezperez
PROGRESSIVE IMAGE RENDERING
@jmperezperez
talk with me for more info
@jmperezperez
By José M. Pérez
This talk addresses the culprit in page size, images. I feel that we like discussing JS and CSS and its payload, but we tend to forget about images, which represent 2/3 of the bytes of a page. The most performant request is the one that is never done, and this beats any possible image optimization. There are some common scenarios in which we can avoid using images, being adopting a minimalism/flat design or hiding certain images on smaller screens. Of course, we will need to use some images eventually. For these cases I explain how the picture element might help and some of its limitations I have found when working with real websites. One of them is lazy loading of images, which can bring great savings in terms of data traffic, but it is also easy to implement wrongly. Lastly I show some techniques to "fill" the space for an image while it is loaded, going from just showing an empty area to doing a progressive loading from a blurry image.