可自定义设置以下属性:
瀑布流的图片数组(imageData),必传
瀑布流要划分的列数(columnCount),默认3
瀑布流各列之间的间隙(columnGap),默认30px
瀑布流区域的总宽度(totalWidth),默认1200px
瀑布流区域背景填充色(backgroundColor),默认'#F2F4F8'
主要使用两种方式实现:
- 方式一:使用css的column-count和column-gap
- 方式二:使用js获取每张图片宽高,结合relative和absolute定位计算每个图片的位置top,left,保证每张图片都追加在高度最小的那列末尾
方式一具体实现如下:(纯CSS,实现简单,但图片顺序是每列从上往下排列)
效果如下图:
①创建瀑布流展示组件Waterfall.vue:
<template>
<div class="m-waterfall-wrap" :style="`background: ${backgroundColor}; width: ${width}px; padding: ${columnGap}px; column-count: ${columnCount}; column-gap: ${columnGap}px;`">
<div class="m-img" :style="`margin-bottom: ${columnGap}px;`" v-for="(item, index) in imageData" :key="index">
<img class="u-img" :src="item.imgUrl" :title="item.title" :alt="item.title" />
</div>
</div>
</template>
<script>
/*
纯CSS,实现简单,但图片顺序是每列从上往下排列
*/
export default {
name: 'Waterfall',
props: {
imageData: { // 瀑布流的图片数组
type: Array,
required: true,
default: () => {
return []
}
},
columnCount: { // 瀑布流要划分的列数
type: Number,
default: 3
},
columnGap: { // 瀑布流各列之间的间隙
type: Number,
default: 30
},
totalWidth: { // 瀑布流区域的总宽度
type: Number,
default: 1200
},
backgroundColor: { // 瀑布流区域背景填充色
type: String,
default: '#F2F4F8'
}
},
computed: {
width () {
return this.totalWidth - 2 * this.columnGap
}
}
}
</script>
<style lang="less" scoped>
.m-waterfall-wrap {
.m-img {
.u-img {
width: 100%;
vertical-align: bottom;
}
}
}
</style>
②在要使用的页面引入:
<Waterfall
:imageData="imageData"
:columnCount="3"
:columnGap="30"
:totalWidth="900"
backgroundColor="skyblue" />
import Waterfall from '@/components/Waterfall'
components: {
Waterfall
}
imageData: [
{
title: 'image-1',
imgUrl: require('@/assets/images/1.jpg')
},
{
title: 'image-2',
imgUrl: require('@/assets/images/2.jpg')
},
{
title: 'image-3',
imgUrl: require('@/assets/images/3.jpg')
},
{
title: 'image-4',
imgUrl: require('@/assets/images/4.jpg')
},
{
title: 'image-5',
imgUrl: require('@/assets/images/5.jpg')
},
{
title: 'image-6',
imgUrl: require('@/assets/images/6.jpg')
},
{
title: 'image-7',
imgUrl: require('@/assets/images/7.jpg')
},
{
title: 'image-8',
imgUrl: require('@/assets/images/8.jpg')
}
]
方式二具体实现如下(主要使用js进行计算,新的图片每次都添加在最短那列的末尾)
效果如下图:
①创建瀑布流展示组件Waterfall.vue:
<template>
<div class="m-waterfall-wrap" :style="`background-color: ${backgroundColor}; width: ${totalWidth}px; height: ${height}px;`">
<img
class="u-img"
v-for="(item, index) in imagesProperty"
:key="index"
:style="`width: ${imageWidth}px; top: ${item && item.top}px; left: ${item && item.left}px;`"
:src="imageData[index].imgUrl"
:title="imageData[index].title"
:alt="imageData[index].title" />
</div>
</template>
<script>
/*
主要使用js进行计算,新的图片每次都添加在最短那列的末尾
*/
export default {
name: 'Waterfall',
props: {
imageData: { // 瀑布流的图片数组
type: Array,
required: true,
default: () => {
return []
}
},
columnCount: { // 瀑布流要划分的列数
type: Number,
default: 3
},
columnGap: { // 瀑布流各列之间的间隙
type: Number,
default: 30
},
totalWidth: { // 瀑布流区域的总宽度
type: Number,
default: 1200
},
backgroundColor: { // 瀑布流区域背景填充色
type: String,
default: '#F2F4F8'
}
},
data () {
return {
imagesProperty: [],
preImages: new Array(this.columnCount)
}
},
computed: {
imageWidth () {
return (this.totalWidth - 4 * this.columnGap) / this.columnCount
},
height () {
return Math.max(...this.preImages) + this.columnGap
}
},
watch: {
imageData (to) {
this.onPreload()
this.imagesProperty.splice(to.length)
}
},
created () {
if (this.imageData.length) {
this.onPreload()
this.imagesProperty.splice(this.imageData.length)
}
},
methods: {
getPosition (i, height) { // 获取图片位置信息(top,left)
if (i < this.columnCount) {
this.$set(this.preImages, i, this.columnGap + height)
return {
top: this.columnGap,
left: (this.imageWidth + this.columnGap) * i + this.columnGap
}
} else {
const top = Math.min(...this.preImages)
var index = 0
for (let n = 0; n < this.preImages.length; n++) {
if (this.preImages[n] === top) {
index = n
break
}
}
this.$set(this.preImages, index, top + this.columnGap + height)
return {
top: top + this.columnGap,
left: (this.imageWidth + this.columnGap) * index + this.columnGap
}
}
},
onLoad (url, i) {
return new Promise((resolve) => {
const image = new Image()
image.src = url
const that = this
image.onload = function () { // 图片加载完成时执行,此时可通过image.width和image.height获取到图片原始宽高
var height = image.height / (image.width / that.imageWidth)
that.$set(that.imagesProperty, i, { // 存储图片宽高和位置信息
width: that.imageWidth,
height: height,
...that.getPosition(i, height)
})
// console.log('this:', this === image) // true 函数运行时所在的对象即img
resolve('load')
}
})
},
async onPreload () { // 计算图片宽高和位置(top,left)
const len = this.imageData.length
for (let i = 0; i < len; i++) {
await this.onLoad(this.imageData[i].imgUrl, i)
}
}
}
}
</script>
<style lang="less" scoped>
.m-waterfall-wrap {
position: relative;
.u-img {
position: absolute;
display: inline-block;
object-fit: contain;
vertical-align: bottom;
}
}
</style>
②在要使用的页面引入:
<Waterfall
:imageData="imageData"
:columnCount="3"
:columnGap="30"
:totalWidth="900"
backgroundColor="skyblue" />
import Waterfall from '@/components/Waterfall'
components: {
Waterfall
}
imageData: [
{
title: 'image-1',
imgUrl: require('@/assets/images/1.jpg')
},
{
title: 'image-2',
imgUrl: require('@/assets/images/2.jpg')
},
{
title: 'image-3',
imgUrl: require('@/assets/images/3.jpg')
},
{
title: 'image-4',
imgUrl: require('@/assets/images/4.jpg')
},
{
title: 'image-5',
imgUrl: require('@/assets/images/5.jpg')
},
{
title: 'image-6',
imgUrl: require('@/assets/images/6.jpg')
},
{
title: 'image-7',
imgUrl: require('@/assets/images/7.jpg')
},
{
title: 'image-8',
imgUrl: require('@/assets/images/8.jpg')
}
]