0x00 前言

最近接到个关于移动端适配的任务,因为希望复用 PC 端的数据所以有些东西实现起来比较麻烦,比如现在讨论的显示图片中心区域。原需求是这样的:在图片比例和要显示的容器比例一致时直接按比例缩放就好,但是如果图片比例和容器比例不一致,这时候就要以容器的比例为准,显示图片的中心区域。原谅我苍白的文字描述,下面直接给图:

需求示例

0x01 如何获取 CSS 设置背景图的大小?

清楚需求后决定将图片以 CSS 的背景图显示,因为依稀记得 background-positionbackground-size 可以设置图片显示的位置和图片缩放比例,因为图片是通过设置 style 显示的,那么现在的问题是怎么能拿到图片的原始大小呢?我们知道 img 元素有 naturalWidthnaturalHeight 属性代表原始图片宽、高。那么现在我们就可以通过 js 获取 CSS 中图片的 url 然后通过 new Image() 并设置 src 不就能通过 naturalWidthnaturalHeight 的到原始图片的宽高了嘛!示例代码如下(依赖 zepto.js):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(function ($) {
// 获取 CSS 背景图片 url 的正则表达式
var urlRegex = /url\(['"]*(.*?)['"]*\)/g;
$.fn.getBackgroundSize = function (callback) {
this.each(function () {
// 背景图片元素
var img = $(this);
// new 一个 image 元素作为模板
var tpl = new Image();
// 根据正则获取 css 背景图 url 并设置为模板图片的 src
tpl.src = img.css('background-image').replace(urlRegex, '$1');
// 待图片加载完毕后执行回调函数,传递背景图片元素对象,图片的原宽度和原高度
tpl.onload = function () {
callback(img, tpl.naturalWidth, tpl.naturalHeight);
tpl = null;
}
})
return this;
}
})(Zepto);

使用方法:

1
2
3
$('.pic').getBackgroundSize(function(img, width, height){
console.log(img, width, height);
}

0x02 如何只显示图片中心区域

现在我们已经知道原图的宽高和容器的宽高了,然后我们可以分别计算出它们的宽高比,通过比较宽高比我们就可以知道如何设置 background-position 的值了。下面先放代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$('.pic').getBackgroundSize(function(img, width, height){
// 容器宽高
boxHeight = img.height();
boxWidth = img.width();
// 容器宽高比
boxScale = boxWidth / boxHeight;
// 图片宽高比
imgScale = width / height;
if (imgScale == boxScale) { // 宽高比一致
img.css('background-position', '0 0');
img.css('background-size', '100% 100%');
} else if (imgScale > boxScale) { // 图片过宽情况
// 计算按高度缩放后图片宽度
width = width * (boxHeight / height);
var offset = String(-(width - boxWidth) / 2);
// 向左移 (图片宽度减容器宽度 ÷ 2) 单位,即显示为图片中心区域
img.css('background-position', offset + 'px');
} else { // 图片过高情况
// 计算按宽度缩放后图片高度
height = height * (boxWidth / width);
// 向下移 (图片高度减容器高度 ÷ 2) 单位,即显示为图片中心区域
var offset = String(-(height - boxHeight) / 2);
img.css('background-position', '0 ' + offset + 'px');
}
});

在图片宽高比与容器一致时 background-position 直接设置为 0 0background-size 设置为 100% 100% 就好了。但是在不一致时 background-size 需要设置为 cover ,因为在图片过宽或过高时设为 cover 会超出容器并因此隐藏起来,这时设置 background-position 才是有效的。不过 background-position 不能直接是原始图片的大小而是经过 background-size 缩放后的大小,所以才会有 width = width * (boxHeight / height); 这样一句计算缩放后宽度的语句。

0x03 总结

上面的代码就基本能够满足需求了,但是这种方式仍然不是完美的解决方案啊。因为图片的中心也未必是最需要展示的内容啊,在一些既不符合比例的图片里还是不会显示重要内容的。还是觉得直接能有符合移动端比例的图比较合适,不过谁叫是老项目呢,要把以前的内容和方式都改了也是很麻烦的说。毕竟 “Done is better than perfect !”,先把砖搬完再说吧~