惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
Webroot Blog
Webroot Blog
AI
AI
P
Palo Alto Networks Blog
Attack and Defense Labs
Attack and Defense Labs
WordPress大学
WordPress大学
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
Spread Privacy
Spread Privacy
T
Tor Project blog
罗磊的独立博客
小众软件
小众软件
S
Security Affairs
酷 壳 – CoolShell
酷 壳 – CoolShell
量子位
Apple Machine Learning Research
Apple Machine Learning Research
T
Threatpost
NISL@THU
NISL@THU
博客园_首页
PCI Perspectives
PCI Perspectives
大猫的无限游戏
大猫的无限游戏
IT之家
IT之家
N
News and Events Feed by Topic
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Forbes - Security
Forbes - Security
博客园 - 叶小钗
D
Darknet – Hacking Tools, Hacker News & Cyber Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
L
LINUX DO - 热门话题
T
Threat Research - Cisco Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
腾讯CDC
Security Latest
Security Latest
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The Cloudflare Blog
A
About on SuperTechFans
爱范儿
爱范儿
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
TaoSecurity Blog
TaoSecurity Blog
宝玉的分享
宝玉的分享
G
GRAHAM CLULEY
雷峰网
雷峰网
F
Full Disclosure
I
Intezer
Cloudbric
Cloudbric
博客园 - 三生石上(FineUI控件)
U
Unit 42

remy sharp's l:inks

Deno Style Guide Easy 6502 by skilldrick Profiling React.js Performance BEM Naming Cheat Sheet by 9elements Visual 6502 Remix Elijah Manor (@elijahmanor) on X All – Tiny Helpers AST explorer Insecure How to Authenticate with Next.js and Auth0: A Guide for Every Deployment Model Revealed: quarter of all tweets about climate crisis produced by bots I Add 3-25 Seconds of Latency to Every Page I Visit GitHub - ericchiang/pup: Parsing HTML at the command line Browserling – Online cross-browser testing Error Handling with GraphQL and Apollo Schwerkraftprojektionsgerät How To Turn Off Catalina Update Notifications (Prompts & Badges) • macReports Maskable.app It’s 2020 and you’re in the future Emulators written in JavaScript | Frederic Cambus The Size of Space systemfontstack JavaScript isn’t always available and it’s not the user’s fault Programming Fonts - Test Drive Screen Size Map GitHub - Yonet/MixedRealityResources: Mixed Reality related resources The Lines of Code That Changed Everything Notes from the Internet Health Report 2019 CopyPalette Generative Artistry Profile a React App for Performance nanoSQL 2 Moving Your JavaScript Development To Bash On Windows — Smashing Magazine Startup Playbook How to Release a Custom React Component, Hook or Effect as an npm Package React Suite - Enterprise React UI Component Library Use VSCode Dimmer to Highlight Code when Teaching 3 simple rules for effectively handling dates and timezones Large collection of how to animate pixelart Accessible React component libraries mineral-ui.com Australian Government Design System Reakit – Toolkit for building accessible UIs Chakra UI Curl Cookbook The problem with tooltips and what to do instead GitHub - trimstray/the-book-of-secret-knowledge: A collection of inspiring lists, manuals, cheatsheets, blogs, hacks, one-liners, cli/web tools and more. It Form design: from zero to hero all in one blog post Brussels changes its mind AGAIN on .EU domains: Euro citizens in post-Brexit Britain can keep them after all Improve Your Email Campaigns with These Fantastic Tools Ethical Web Principles Old Typewriter Text Effect Bruce Lawson Google open sources standardized code in bid to become Mr Robots.txt Preferreds-color-scheme:你好,黑暗,我的老朋友  |  Articles  |  web.dev Free for Developers Startup idea checklist Developing a Robust Font Loading Strategy for CSS-Tricks—zachleat.com How I use Slack—alone—to get more done Upcoming proposals in JavaScript 🦄 VueJS is dead, long live VueJS! Bruce Lawson Answers for young people - Tim Berners-Lee Finally, an AI that can reliably catch and undo Photoshop airbrushing. Who made it? Er, Photoshop maker Adobe Qubyte Codes - How I schedule posts using GitHub Actions Games and Graphics in Popup URL bars Stack 58 bytes of css to look great nearly everywhere The reduce ({...spread}) anti-pattern - RichSnapp.com The Online Disassembler CSS Gradient – Generator, Maker, and Background Chrome DevTools  |  Chrome for Developers HB88 | Đăng Nhập HB88 Cá Cược Nhanh Chóng Không Bị Chăn #1 Introducing React Apollo 2.1 - Apollo GraphQL Blog Color Palettes Generator and Color Gradient Tool Clément Chastagnol ~ Moving efficiently in the CLI Testing Async Components · Issue #346 · enzymejs/enzyme Game Platforms recent news | Game Developer Professional Sound Effects - Royalty-Free SFX | Unlimited Downloads entr(1) Learn React From The Comfort Of Your Browser Axe Rules | Deque University Record and share your terminal sessions, the simple way Web Design Museum Powerful New Additions to the CSS Grid Inspector in Firefox Nightly – Mozilla Hacks - the Web developer blog Latency | Apex Software HackerNoon Signals and Sine Waves (Learn Web Audio from the Ground Up, Part 1) JavaScript Systems Music Vintage bits on cassettes Microsoft Design Legal Documents & Contract Templates | Simply Docs create-graphql-server — instantly scaffold a GraphQL server GraphQL and MongoDB — a quick example Using GraphQL with MongoDB Your First GraphQL Server Build a GraphQL server from scratch Practical Redux, Part 3: Project Planning and Setup Freesound
Dominant Colors for Lazy-Loading Images | manu.ninja
Manuel Timelthaler · 2016-04-13 · via remy sharp's l:inks

Pinterest, Google Images and lots of image-heavy sites lazy-load their content. They also calculate the dominant color of each image to use as a placeholder. This post presents a few methods to do the same and helps you understand the GIF file format to make the most of data URIs.

The basic concept is to use a tiny blank.gif as src attribute and replace it with the correct image after the page has fully loaded. The blank.gif can also be set as a Base64-encoded Data URI to save a request.

<img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
data-src="https://s-media-cache-ak0.pinimg.com/474x/50/1b/74/501b74902935b063816ea8e14f460ca0.jpg"
alt="Ghost In The Shell">

Pinterest then sets the style of the wrapper to background: #1e1f20; and shows the image with opacity: 1; when it has loaded. They could therefore easily animate the transition, but right now they don’t.

Finding the Dominant Color of an Image

Finding the dominant colors of an image requires clustering of points in three-dimensional space. I initially planned to indulge in clustering algorithms and write my own k-means clustering in JavaScript, but after installing GraphicsMagick for decoding image files of various formats on the server I decided to put this plan off to another day and simply use the color quantization of GraphicsMagick.

You are of course free to compare the results of even more sophisticated algorithms and choose the one that is to your liking, but If you want a simpler solution the color quantization of GraphicsMagick or ImageMagick is usually sufficient.

Node.js

The following snippet shows you how to use the gm npm package for finding the dominant color. It is a good idea to resize the image first to soften compression artifacts. This will also speed up the quantization as there is less data to process – quantization of a 12 megapixel image (iPhone 6s) takes 13-17 seconds in my benchmarks, whereas first resizing it reduces the time to 3-5 seconds.

var gm = require('gm');

gm('test.jpg')
.resize(250, 250)
.colors(1)
.toBuffer('RGB', function (error, buffer) {
console.log(buffer.slice(0, 3));
});

PHP

The same can of course be accomplished with the imagick extension in PHP. I do know that a gmagick extension exists but the former was already installed on my server.

<?php

$image = new Imagick('test.jpg'));
$image->resizeImage(250, 250, Imagick::FILTER_GAUSSIAN, 1);
$image->quantizeImage(1, Imagick::COLORSPACE_RGB, 0, false, false);
$image->setFormat('RGB');
echo substr(bin2hex($image), 0, 6);

Deep Dive into GIFs and Base64-encoded Data URIs

Let’s say you have calculated the dominant colors for all your images and your lazy-loading is working smoothly. You can now go a step further and use a different Base64-encoded placeholder for each image, so that you don’t need wrappers and the img element itself can be its placeholder. To do this you have to either create lots of GIFs and store them somewhere or create them on the fly, which is what I’d like to explain in this section.

If you fire up Photoshop, create a file with 1 × 1 pixels in a single color and hit Save For Web you get a GIF which is exactly 43 bytes. I have labeled the binary data in the following snippet for you.

47 49 46 38 39 61             // Header
01 00 01 00 80 00 00          // Logical Screen Descriptor
FF FF FF 00 00 00             // Global Color Table
21 F9 04 00 00 00 00 00       // Graphics Control Extension
2C 00 00 00 00 01 00 01 00 00 // Image Descriptor
02 02 44 01 00                // Image Data
3B                            // Trailer
data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==

If you go back to the first snippet in this article you’ll notice that the Base64-encoded data URI is a lot longer than Pinterests’s. The graphics control extension and the trailer are actually optional. So if you remove them you get a tiny GIF, which is only 34 bytes.

47 49 46 38 39 61             // Header
01 00 01 00 80 01 00          // Logical Screen Descriptor
FF FF FF 00 00 00             // Global Color Table
2C 00 00 00 00 01 00 01 00 00 // Image Descriptor
02 02 44 01 00                // Image Data
data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBAA==

How did Pinterest get a GIF with only 26 bytes? Turns out that you can remove the global color table and the LZW-encoded image data as well. Browsers then just assume a color, which is usually black.

data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=

The last thing I want to mention is that Pinterest does not remove the trailer. On the one hand Photoshop, GIMP and possible some browsers report an Unexpected End of File error if there is no trailer present. On the other hand adding it back in does not increase the size of the Base64 string. Why? A Base64 string’s length is always a multiple of 4 bytes. The equals symbol is used as a padding at the end of the string. So if you remove the trailer the Base64 string will end in AA== but still have 26 bytes.

data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACAA==

Creating Tiny Single-Colored GIFs

The following snippet takes the above knowledge and creates data URIs in the dominant color of a given image. You can achieve the same in PHP by using the pack and base64_encode functions.

var gm = require('gm');

var header = new Buffer('474946383961', 'hex');
var logicalScreenDescriptor = new Buffer('01000100800100', 'hex');
var imageDescriptor = new Buffer('2c000000000100010000', 'hex');
var imageData = new Buffer('0202440100', 'hex');

gm('test.jpg')
.resize(250, 250)
.colors(1)
.toBuffer('RGB', function (error, buffer) {
var gif = [
header,
logicalScreenDescriptor,
buffer.slice(0, 3),
new Buffer([0, 0, 0]),
imageDescriptor,
imageData
];
console.log('data:image/gif;base64,' + Buffer.concat(gif).toString('base64'));
});

data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA==

Tiny Thumbnails

You can now lazy-load your images and show a tiny GIF in the dominant color as a placeholder, which is embedded in your HTML as a Base64-encoded data URI. The last thing I want to show you is how easily you can now implement the placeholders Medium is using.

If you resize your image to 3 × 3 pixels and remove the color quantization you get a data URI that is only a bit longer than the single-colored blank.gif but gives you more of a thumbnail for your image. If you then resize your thumbnail to the image dimensions you may have to use filter: blur(…); to soften artifacts but you can see in the image below that Chrome does an excellent job in upscaling tiny thumbnails.

var gm = require('gm');

gm('test.jpg')
.resize(3, 3)
.toBuffer('GIF', function (error, buffer) {
console.log('data:image/gif;base64,' + buffer.toString('base64'));
});

data:image/gif;base64,R0lGODlhAwACAPIFAD1KI0JSIWp2WXOIj4WVlYicngAAAAAAACH5BAAAAAAALAAAAAADAAIAAAMESDUSkAA7

That’s all for now. If you like this article please share or retweet. I’d also love to hear your feedback and will answer any questions.

WordPress Plugin

I’ve started working on a WordPress plugin called Dominant Colors Lazy Loading which uses the above concepts. The code is on GitHub and I'd very much appreciate your feedback and pull requests.

References

What’s In A GIF

The Tiniest GIF Ever

GIF File Format Summary

GraphicsMagick for node.js