最近项目中需要处理与图片相关的布局,不得不说图片这玩意真想要得到完美的展示效果还真是要费些力气。因为图片的尺寸或者比例各不相同。所以想要不同尺寸的图片有好的显示效果,你就需要找到适合的方式。
而且图片往往是不可或缺元素。毕竟一图胜千言,有时候图片能给带来非常好的效果。
比如我们每天都会使用的表情包,它往往能够表达出我们无法用文字描述的信息,还比如我们经常在公众号里看到的漫画虽然短短几个字,但是却能够让我们看的不亦乐乎。
当然如果我们做图片网站的,那图片的处理就是绕不开的话题了。因对图片的处理经验不多,所以就边学边用。今天就把最近学习与图片相关的知识整理出来。
比如单个图片如何更好的展示,瀑布流布局都有哪些你不知道的实现方式。
接下来我们就直接进入正题,我们先从单张图片的展示说起
设置宽或高 100%
因为图片其本身的独特性:
1.不设置宽高的情况下会按原有的尺寸显示在网页中。即有多大,显示多大。
2.在非等比缩放的情况下会被拉伸变形。
3.设置宽度或者高度时,会保持原宽高比进行缩放。
显然当我们采用 1、2 种方式的时候破坏性很强,无法应用到实际的项目中去。
所以往往我们会在项目中使用第 3 种方式,即设置高度或者宽度。它会保持原有比例进行缩放。
但是问题又来了,图片要么超出容器,要么就会留有空白,除非容器的宽高比恰好等于图片的宽高比时,才会完全贴合。
对于超出容器的图片我们可以使用 overflow: hidden 把超出部分隐藏。图片得到了好的展示效果。但相应的我们也损失了图片的一部分可视区域。
所以这个时候就需要你根据需求进行取舍了,到底是选择隐藏图片的一部分,还是留有空白。有的小伙伴会说,我们产品说了,图片变形没问题,你就给我充满容器就行了。好吧…
即使如此,你也要把这篇文章好好读一读,因为需求是千变万化的,保不齐哪一天就需要了。
又有小伙伴说,这 2 种都不符合我们的产品需求怎么办,还有其他的方式吗?答案是必须的,一起来看。
object-fit
CSS3 的 object-fit 属性是用来指定「可替换元素」的内容是如何适应到容器中的。它的值有 5 种。分别为:fill | contain | cover | none | scale-down
fill:会充满整个容器,不考虑宽高比,所以会被拉伸变形。
contain:会缩放到容器内,保持宽高比。
cover:会保持比例进行缩放,内容的尺寸一定会大于等于容器,然后进行裁剪。
none:保持图片的原始尺寸。
scale-down:会在 none 或 contain 中选择一个,原则是:当容器小时,它的表现和 contain
一样;当图片小时,它的表现和 none 一样。即谁小选择谁。
瀑布流布局即不会出现错乱现象,而且会最大限度显示图片的内容。所以是众多图片网站选择的布局方式。
而瀑布流布局目前有两种形式:一是等宽型,二是等高型。我们先来说说等宽型。
思路1. JS 计算列数
关键思路:
首先设置列宽度,然后计算能够展示的列数。
向每一列中添加图片。
<script>
export default {
methods: {
//计算图片列数
getColNumbers() {
let clientWidth = this.$refs.waterfall.clientWidth
this.colNumbers = Math.floor(clientWidth / this.colWidth)
},
//读取图片
loadImage() {
this.getColNumbers()
for (let i = 0; i < 17; i++) {
let colIndex = i % this.colNumbers
let url = require(`@/assets/images/${i}.jpg`)
if (this.imgList[colIndex]) {
this.imgList[colIndex].push(url)
} else {
this.$set(this.imgList, colIndex, [url])
}
}
},
}
}
</script>
优势:思路清晰简单,不需要做过多的计算,只要计算需要显示的列数然后添加图片即可。
劣势:每列的末尾可能不够友好,可能出现有些列会很长,有些又会很短。
思路2. 利用绝对定位
关键思路:
首先设置列宽度,然后计算能够展示的列数。
把图片设置为绝对定位,然后计算出每个图片的top,left值。
先把第一行图片排好,top 为 0,left 为 列的索引*列宽。
从第二行开始,每张图片都放到最短的一列下面。然后增加此列高度,此时列的高度发生变化,下张图片又会寻找其他最短的列。以此持续计算下去。
<script>
export default {
methods: {
//计算图片列数
getColNumbers() {
let clientWidth = this.$refs.waterfall.clientWidth
this.colNumbers = Math.floor(clientWidth / this.colWidth)
},
//读取图片
loadImage() {
this.getColNumbers()
for (let i = 0; i < 17; i++) {
let image = new Image()
let url = require(`@/assets/images/${i}.jpg`)
image.src = url
image.onload = () => {
this.render({
index: i,
url: url,
ratio: image.width / image.height
})
}
}
},
render(imgInfo) {
let colIndex = imgInfo.index % this.colNumbers
imgInfo.left = colIndex * this.colWidth
//首行 top为 0,记录每列的高度
if (imgInfo.index < this.colNumbers) {
imgInfo.top = 0
this.colHeight[colIndex] = this.colWidth / imgInfo.ratio
} else {
//获取高度的最小值
let minHeight = Math.min.apply(null, this.colHeight)
let minIndex = this.colHeight.indexOf(minHeight)
//此图片的 top 为上面图片的高度,left 相等
imgInfo.top = minHeight
imgInfo.left = minIndex * this.colWidth
//把高度加上去
this.colHeight[minIndex] += this.colWidth / imgInfo.ratio
}
this.imgList.push(imgInfo)
}
}
}
</script>
优势:因为每次追加的图片都是最短列,所以末尾的展示会比思路 1 中要友好很多。
劣势:没渲染一张都会计算一次 top,left 值。而且图片的顺序是打乱的。
思路3. CSS3 column 属性
关键思路:
column-count:指定列数
column-gap: 设置列之间的间距
<template>
<div class="waterfall-width-column">
<div class="image-box" v-for="img in imgList" :key="img">
<img :src="img" alt="" />
</div>
</div>
</template>
<style lang="scss" scoped>
.waterfall-width-column {
column-count: 3;
column-gap: 10px;
.image-box {
img {
display: block;
width: 100%;
}
}
}
</style>
优势:更加简单,不用额外计算,直接使用CSS渲染高效。
劣势:图片的顺序是从上向下排列的,这个要看业务需求允不允许了。另外列数固定。
不过你可以尝试通过媒体查询设置不同列数
@media (min-width: 768px) {
.waterfall-width-column {
column-count: 3;
}
}
@media (min-width: 992px) {
.waterfall-width-column {
column-count: 4;
}
}
@media (min-width: 1200px) {
.waterfall-width-column {
column-count: 6;
}
}