我们在日常使用移动APP的时候,特别是一些资讯类的APP,都会有图片展示的相关UI,例如今日头条APP的单大图和三图模式,如下图:
单图:
三图:
或者是类似微博或者朋友圈这种9宫格的图片展示效果,如下图:
对于这些图片,如果你单纯的以为直接用几个<img>
,配置一下src地址,然后渲染在页面上,那就大错特错了😂,对于这种类型的UI展示,我们需要明确下面几点:
- 图片在上传后,会有不同的大小,有的是长图(长大于宽),有的是宽图(宽大于长),或者是一些接近正方形的图片。
- 在保证图片原本长宽比的情况下,要想将图片显示在一个正方形的区域内,或者是固定长宽的区域内,是必须要截取一部分图片展示的。
- 采用CSS或者JavaScript都可以实现这种效果。
大家可以看下面这几张图,红色的是图片本身,虚线框就是展示出来的区域,便于理解:
宽图:
长图:
CSS的background-size
:
单独使用CSS的话,我们可以轻松的实现上面这个效果,主要利用到的属性就是background-size
这个属性,可以先从概念上了解一下这个属性:
background-size: length|percentage|cover|contain
-
length:设置背景图像的高度和宽度。第一个值设置宽度,第二个值设置高度。如果只设置一个值,则第二个值会被设置为 “auto”。
-
percentage:以父元素的百分比来设置背景图像的宽度和高度。第一个值设置宽度,第二个值设置高度。如果只设置一个值,则第二个值会被设置为 “auto”。
-
cover:在保持图像的纵横比的前提下,以适合铺满整个容器并将图像缩放成将完全覆盖背景定位区域的最小大小。优点是背景图片全部覆盖所属元素区域;缺点是超出的部分会被隐藏。
-
contain:与cover相反,在保持图像的纵横比的情况下,以适合铺满整个容器,并将图像缩放成将适合背景定位区域的最大大小。优点是图片不会出现变形,同时背景图片被完全展示出来;缺点是当所属元素的宽高比与背景图片的宽高比不同时,会出现背景留白。
我们可以采用background-size:cover;
比较合适,在保证纵横比的情况下,如果图片超过背景区域,将多余部分隐藏即可,同时设置background-position: center center;
将主要内容居中显示。
CSS的object-fit
:
HTML5新增的<img />
标签的属性object-fit
也可满足需求,但是要注意兼容性。
object-fit: fill|contain|cover|scale-down|none|initial|inherit;
主要用到以下属性:
-
fill:默认,不保证保持原有的比例,内容拉伸整个内容容器。
-
contain:保持原有尺寸比例,内容被缩放,参考background-size:contain。
-
cover:保持原有尺寸比例,但部分内容可能被剪切,参考background-size:cover。
-
scale-down:保持原有尺寸比例。内容的尺寸与 none 或 contain 中的一个相同,取决于它们两个之间谁得到的对象尺寸会更小一些,更加智能。
代码效果demo:
首先大家可以打开
https://app.nihaoshijie.com.cn/mypro/imagedemo/index.html
来看一下效果。
如果是一个单大图,我们可以直接给一个div设置background-image
,然后设置background-image
即可,代码如下:
<div class="one-img"></div>
.one-img {
width: 100%;
padding-top: 50%;
background-image: url('https://gpic.qpic.cn/gbar_pic/osL7w6JTehzgKuaKrPEJ8V3lia1zoLaPShY05MdBofOpBye0yNpRXYA/');
background-size: cover;
background-position: center center;
}
效果如下图:
代码中使用的图片来源于网络
这里有一个知识点,我们如果想要实现屏幕的适配,div的长宽是绝不可以写成固定值px的,所以宽度可以设置成100%,这样如果在大屏幕下,图片自身会变大,但是高度我们是无法设置一个合适的百分比的,这里我们借助了padding-top
属性,将padding-top
设置成百分比,可以让一个div的高度被撑开,具体的值依据宽度的值,即50%表示是宽度(width:100%)的50%。
三张连续图,代码如下:
<div class="three-img-other-wrap">
<div class="three-img-other-1 img-1"></div>
<div class="three-img-other-2 img-2"></div>
<div class="three-img-other-3 img-3"></div>
</div>
.three-img-wrap {
margin-top: 5px;
width: 100%;
overflow: hidden;
}
.three-img {
float: left;
width: 33.3333%;
padding-top: 33.3333%;
border-right: 1px solid #fff;
background-size: cover;
background-position: center center;
box-sizing: border-box;
}
效果如下:
每个div,设置float:left
来实现横向平铺,注意一下这里不建议使用display:inline-block
,会出现都得空隙,如果想要实现9宫格,将这3个复制2份即可。
或者是另外一种3+2+1显示方式图,代码如下:
.three-img-other-wrap {
margin-top: 5px;
width: 100%;
overflow: hidden;
}
.three-img-other-1 {
width: 66.6666%;
padding-top: 66.6666%;
float: left;
border-right: 1px solid #fff;
background-size: cover;
background-position: center center;
box-sizing: border-box;
}
.three-img-other-2 {
width: 33.3333%;
padding-top: 33.3333%;
float: left;
border-bottom: 1px solid #fff;
background-size: cover;
background-position: center center;
box-sizing: border-box;
}
.three-img-other-3 {
width: 33.3333%;
padding-top: 33.3333%;
float: left;
background-size: cover;
background-position: center center;
box-sizing: border-box;
}
<div class="three-img-other-wrap">
<div class="three-img-other-1 img-1"></div>
<div class="three-img-other-2 img-2"></div>
<div class="three-img-other-3 img-3"></div>
</div>
效果如下:
图片之间的缝问题:
从上面的效果图来看,每张图片之间都有一定的间距(一般是1px-3px之间),在这里我们如果使用margin来实现的话,我们是无法设置一个具体的数值的,因为我们的长宽都是采用百分比,margin也必须采用百分比,否则会出现错乱,但是在此场景下margin不适合采用百分比,所以我们采用border边框来模拟这个间距:
border-right: 1px solid #fff;
box-sizing: border-box;
需要注意box-sizing: border-box;
,这样border将的长度将会计算在整个宽度里面,即border+width等于具体的设置的百分比。
采用JavaScript来实现:
其实从代码的优雅程度上来说,采用我们上面讲解的纯Css的方法是比较好的一种方法,但是也有弊端:
1. 无法监听图片的加载成功和失败事件,例如onerror
或者onload
。这会导致我们无法给加载失败的图片一个默认的显示图。
2. 我们在实现图片懒加载的逻辑时,div+background-image
这种方式相交于<img>
方式需要写更多的逻辑。
在这里给大家介绍一下div+background-image
和<img>
之间的区别:
在网页加载的过程中,以css背景图存在的图片background-image会等到结构加载完成(网页的内容全部显示以后)才开始加载,而html中的标签img是网页结构(内容)的一部分会在加载结构的过程中加载,换句话讲,网页会先加载标签<img>
的内容,再加载背景图片background-image,如果你用引入了一个很大的图片,那么在这个图片下载完成之前,<img>
后的内容都不会显示。而如果用css来引入同样的图片,网页结构和内容加载完成之后,才开始加载背景图片,不会影响你浏览网页内容。
如果我们想要用JavaScript加<img>
,来实现这种效果,基本逻辑是:
- 首先需要知道图片的宽高。
- 给每个
<img>
设置src之后,需要同时设置一个父div用来包裹这个<img>
。 - 同时父div需要设置
overflow:hidden
,然后根据外框的大小,和图片的宽高,动态设置<img>
的margin或者left,top来产生位移。
这里的核心是如何根据外框的宽高来动态计算出图片的位移,我们可以封装一个方法来计算,具体的逻辑可以看注释:
getImagePosition(img, cW, cH) {
// cW为外框宽度,// cW为外框高度,
img.marginTop = img.marginLeft = 0;
// img.h表示图片本身高度,img.height表示计算设置之后的高度
// img.w表示图片本身高度,img.width表示计算设置之后的高度
img.width = cW;
img.height = cH;
// 长图 优先设置宽度,然后长图居中
if (img.h * cW / img.w > cH) {
img.height = img.h * cW / img.w;
img.marginTop = (cH - img.height) * 0.5 // 0.5表示居中
} else {// 宽图 优先设置高度度,然后宽图居中
img.width = img.w * cH / img.h;
img.marginLeft = (cW - img.width) * 0.5 // 0.5表示居中
}
return img;
}
在计算出图片位移后,外框的宽高也可以使用JavaScript来动态设置,例如屏幕宽度的三分之一或者是图片宽度的三分之二,代码如下:
document.body.clientWidth * 0.5
document.body.clientWidth * 2 / 3
在众多的移动web技能中,图片居中处理是一个非常重要的技能,也是用的比较多的一种技能,当然还有一些极端情况例如遇到面条图,或者是长宽小于10px的这种非常小的图片,可能需要单独特殊逻辑处理了。当然如果你想要了解更多的移动web技能,欢迎订阅这篇专栏,感谢支持。
本文向您推荐了慕课网的专栏课程《从0到1 实战朋友圈移动Web App开发》,希望小伙伴们能通过这门课程收获满满,祝大家学习进步。
热门评论
又学到了~开心~继续加油~
哈哈,很实用!
可以的,CSS实现的非常优雅,但希望作者说说 padding-top 设置的原理