实现类似Pinterest 的图片预加载功能

提起Pinterest,大家第一印象可能是图片社交网站,里面有很多用户上传的各式各样的图片。从前端设计出发的话,我们一定不会忘记我们曾经非常流行的瀑布流布局。但是今天,给大家简要分析上 Pinterest上另外一项非常值得借鉴图片加载细节。

看看下面的截图:

大家可以感觉到图片出来的时候预先绘制轮廓,重点是预制区域的颜色采用与图片较为相似的色彩值,当图片加载完全后,会有种渐入的效果。 效果体验

其中谷歌的图片搜索也用到了类似效果:

我们称之为这种效果为Color Placeholder [色彩预置],当图片加载的时候,我们优先显示其所在容器的背景颜色(如同很多会显示一个加载的gif),由于受限于不同的图片和大小,因此相比与齐刷刷的加载gif,不同色块体验 可能 更好吧(至少Pinterest Google这么认为吧).

实现步骤

接下来我们进入正题,如何自己实现这样的动画加载效果(实现的方式肯定有很多的也欢迎大家提出更好的思路)

我们先定义下基本的html结构

<!--一个post当作一个单位-->
<div class="post">
  <div class="image-bg" style="background-color:#141646">
    <img width="310" height="242" src="https://mir-s3-cdn-cf.behance.net/projects/404/89388038777855.Y3JvcCwxMDk1LDg1NiwyNTIsMjE.png" />
  </div>
  <p class="title">Mars</p>
</div>

再看下css设置

.image-bg{
    background: #e1e1e1;
  }
  img {
    width:100%;
    opacity: 0;
    transition: opacity .2s ease-in .25s;
    
  }
  .loaded img {
    opacity: 1;
  }

图片默认是透明度为0,当加载完成后设置为1就行啦。

 $(function() {
    $('.post img').each(function() {
       var el = this;
       var image = new Image();
       image.src = el.src;
       
       image.onload = function() {
         $(el).parent().addClass('loaded');
       }
    })
    
  })

DEMO

大概基本思路就是这些,但是这里面最核心的就是确定所谓的Dominant Color(图片中主要色彩)。

设置背景的颜色

如果你用photoshop打开一张图片的话,你只需要几步就可以确定你希望得到的颜色: 滤镜 -> 模糊 -> 平均即可。

当然这是针对你所能处理的图片,如果面对海量的图片的话,这个时候我们需要用程序去实现。

寻找到一张图片较为明显的颜色,需要在三维空间中找到一些聚合的点。如果自己写的话,需要去了解一些聚合算法。当然自己并不打算去写更多的内容关于如何去进行图片的这些颜色的生成,这恐怕不是一篇文章能给说完的。实际上你安装ImageMagick就可以简单的实现预期效果:

convert path/or/url/to/image.png -resize 1x1 txt:-  

但是这个不太适合我们写程序的。我们可以使用第三方的npm gm

var gm = require('gm');

gm('demo1.png')
    .resize(120, 120)
    .colors(1)
    .toBuffer('RGB', function (error, buffer) {
        console.log(buffer.slice(0, 3));
    });

运行输出效果如下:

~ node gm.js
./demo1.png:
<Buffer 34 29 3b>
./demo2.png:
<Buffer cf c3 ad>

对比图如下:

因此借助程序,我们可以在保存图片的时候进行颜色采集,代码中通过先将图片进行大小调整,实际是出于性能的考虑。有助于节约运算时间。除此之外embed.ly也开放了对应的API,方便你获取网络图片的主要色彩。

如果我们能够有途径获取这样的颜色的话,自然整体功能就没有什么难度了。

扩展

其实除了纯粹的颜色背景外,我们还可能会遇到类似 medium 的图片(参考上图)预加载技术,才开始图片是模糊的。实际上我们可以通过插件生成一张几素的小图片,然后运用上高四模糊滤镜,然后等待原图加载完毕后,我们在显示原来的图片。

var gm = require('gm');

gm('demo1.png')
    .resize(4, 4)
    .toBuffer('GIF', function (error, buffer) {
        console.log('data:image/gif;base64,' + buffer.toString('base64'));
    });
<div class="image-bg" style="background-color:#141646">
    <img  src="data:image/gif;base64,R0lGODlhBAADAPMJACwlPjAmPDUqOzgrOgQPSgkSShAVRhEWRplcFsR3EAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAAEAAMAAAQJ0" width="310" height="242" real-src="https://mir-s3-cdn-cf.behance.net/projects/404/89388038777855.Y3JvcCwxMDk1LDg1NiwyNTIsMjE.png" />

如果你有兴趣,可以了解更为复杂的Facebook关于图片预加载的方案Facebook’s 200 byte technique

参考

http://blog.learningspaces.io/color-placeholders-for-loading-images/

https://manu.ninja/dominant-colors-for-lazy-loading-images

http://docs.embed.ly/docs/color-background-placeholders-for-loading-images

https://jmperezperez.com/medium-image-progressive-loading-placeholder/

http://home.deib.polimi.it/matteucc/Clustering/tutorial_html/

http://home.deib.polimi.it/matteucc/Clustering/tutorial_html/