网页自适应显示WebP图片

生命不息,折腾不止。

起因

最近上传了个背景图,加载的非常慢,才发现上传了个1M多的图。优化手段无非就是做做图片裁剪已经质量压缩,网上确实也有不少的分析压缩的方法,但基本都是jpeg和png等。偶然发现最近流行的WebP格式,又是对图片压缩的一大进步,可以大幅优化流量。

WebP就不多做介绍了,有兴趣的百度之。总之这个格式比等质量的png和jpg图片要小不少。但是作为浏览器的通病,各个浏览器未必能够支持,据介绍当前chrome和opera已经支持了,IE系肯定暂时没有支持。要使用这种图片看来得做适配,大致我搜索到的有两种方法:

  • 采用插件支持,让页面支持WebP格式的解码(应该是通过flash)
  • 采用HTML通过picture标记实现两种图片加载
  • 懒加载,通过JavaScript控制加载的资源类型

以上方式是客户端常用的方式,不过像第二种方式需要刻意去修改HTML源码不是很方便,其他方式还行。当然除了上述方式外,还有一些在服务端可以处理的办法,首先是我根据实际测试总结出来的。

nginx处理方法

一般来讲,浏览器在请求服务端时候会携带支持的格式,目前支持webp的浏览器都会在请求Accept中携带 image/webp,这是最简单的服务端检测方法,但是,总不能在代码中去干这个事情吧,况且本站还是静态博客。这个时候想到了牛逼的nginx,nginx有丰富的插件,在不了解有没有类似的插件的情况下完全可以去写一个。使用类似rewrite的功能去跳转。

查询资料发现应该可以通过nginx配置try_files加正则表达式的方式搞定,但是对nginx这块语法不是特别熟练外加配置写的一直不正确,暂时用nginx的lua插件测试下。需要准备一个正常图片如1.png,另外生成对应的webp格式如1.png.webp,统一访问webp资源。nginx根据Accept进行判断是否支持WebP,如果不支持跳转到非webp版本,即去掉webp后缀即可。

默认安装的nginx是没有lua功能的,需要单独编译,这里我偷懒直接使用老罗捐助过的 OpenResty。在CentOS下可以直接添加repo进行yum安装,详情参见http://openresty.org/cn/linux-packages.html,安装完成之后,配置 /usr/local/openresty/nginx/conf/nginx.conf,在location下添加类似如下代码(其实是一段lua)

rewrite_by_lua_block {
local m, _ = ngx.re.match(ngx.var.uri, "^.*?\\.(webp)$")
if m then
local a = ngx.req.get_headers()["Accept"]
local i, _ = string.find(a, "image/webp")
if i == nil then
local u, _ = string.gsub(ngx.var.uri, ".webp", "")
ngx.req.set_uri(u)
end
end
}

其他根据实际需要配置。配置完成并启动nginx之后,写一个测试工程 test.htmltest.jpgtest.jpg.webp 资源并部署到html目录。

<html>
<body>
<img src="test.jpg.webp" />
</body>

除了上面笨拙的办法,Google大神们还提供了个pagespeed模块。这个模块甚是强大,可以对网页进行速度优化,比如合并CSS,压缩空白,合并javascript等等,当然在支持webp的浏览器上会直接返回webp格式,更牛逼的是此过程是自动化的(包括webp格式的生成),不需要人为处理。

PageSpeed模块官网 https://developers.google.com/speed/pagespeed/module/

当然也是需要重新编译nginx的,并且在nginx配置中启用该功能

# 启用ngx_pagespeed
pagespeed on;
pagespeed FileCachePath /tmp/cache/ngx_pagespeed_cache;

启用之后速度杠杠的,当然据说(想想也是)副作用就是服务端的CPU和内存也很高,有点得不偿失。

本站方案

本站是hexo静态博客,主要的大图片在markdown博客中,我打算采用picture标记的方式实现,picture标记的思路是采用H5的picture标记来设置图片资源,如

<picture class="picture">
<source type="image/webp" srcset="image.jpg.webp">
<img class="image" src="image.jpg">
</picture>

如果浏览器支持WebP格式则加载 image.jpg.webp,否则加载image.jpg。那么首先就是需要将hexo生成的代码修改为生成如上代码的方式。hexo采用的是marked引擎,好在修改比较简单,打开目录下 node_modules/marked/lib/marked.js,找到 Renderer.prototype.image 函数实现,修改为以下版本

Renderer.prototype.image = function(href, title, text) {
var re = new RegExp("([a-zA-Z0-9-_]+)\.(png|jpg|gif|jpeg)\.webp$");
if(re.test(href)){
var out = '<picture class="picture">'
out += '<source type="image/webp" srcset="'+ href +'">';
out += '<img class="image" src="'+ href.substring(0, href.length-5);
out += '" alt="' + text + '"';
if (title) {
out += ' title="' + title + '"';
}
out += this.options.xhtml ? '/>' : '>';
out += '</picture>';
return out;
} else {
var out = '<img src="' + href + '" alt="' + text + '"';
if (title) {
out += ' title="' + title + '"';
}
out += this.options.xhtml ? '/>' : '>';
return out;
}
};

当然和之前一样,约定我们写markdown文档时候设置的图片是webp格式的,同时保留原始图片,如 1.png1.png.webp。这样修改之后,hexo生成出来的代码可以根据markdown里面的图片进行适配。

剩下的问题就是样式问题了,如果有特别的图像样式,需要修改原来的主题中的img样式进行迁移。将原来的img样式迁移到 .image 。因为我是博客文章中使用基本没什么问题,如果要完全使用,还需要注意CSS设置的图片(如背景图片)并不能自适应,请注意!

背景图片

如上在CSS中无法处理实现,在Hexo博客中采用的框架模式,那么进行统一的框架处理即可,但是WebP的支持情况就是在浏览器端才可以确认,因此在页面框架中增加一段加载完成后执行的JavaScript代码,用来检测和设置背景图片。

var side = document.getElementsByClassName("panel-cover")[0];
function checkWebp() {
try{
return(document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0);
}catch(err) {
return false;
}
};

if(checkWebp()) {
side.style.backgroundImage = "url('/images/background-cover.jpg.webp')";
} else {
side.style.backgroundImage = "url('/images/background-cover.jpg')";
}

当然,具体的图片是在框架中根据配置生成的,大致原理如此,基本可以实现全站大图的WebP支持,当然小图片太多的话并且这么设置的话还是非常麻烦的,使用原始的格式即可。

最近的文章

C++集成zookeeper客户端

zookeeper是Java下常用的分布式应用程序协调服务。因接触到Java业务系统的一些架构,比如流行的微服务架构等等都采用zookeeper做服务发现。某些系统服务可以采用其他语言如C\C++实现来提升性能或者实现某些特殊功能。 本文为C++集成zookeeper客户端的开发调研。 开始zook …

技术 继续阅读
更早的文章

博客迁移Hexo

用了大半年的Ink Paper静态博客引擎,时间长了就感觉风格有点过于素雅了,感觉一点色彩都没有(文档列表基本也没设置图),而且不知为何官网都无法访问了,不知何故。闲话不说,首先谈谈我用这个GO语言开发的博客系统使用这么长时间的感受 简洁,非常适合阅读,基本纯黑白文字 主题太少,貌似就两个主题,而 …

技术 继续阅读