H5组件 — 双列图片瀑布流的实现
前期准备
1、环境:vue2、vue3代码环境
2、安装vant3(或者最新版本),主要用于实现图片懒加载功能。
运行实例
话不多说上代码:
html部分
<template>
<div class="waterfall-container" ref="container">
<div
v-for="(item, index) in computedList"
:key="index"
class="img-item"
:style="{
width: item.width + 'px',
height: item.height + 'px',
top: item.top + 'px',
left: item.left + 'px'
}"
>
<img
:src="item.src"
lazy-load
:style="{ width: '100%', height: '100%' }"
@load="handleImageLoad(index)"
/>
</div>
</div>
</template>
简析
- 瀑布流容器(waterfall-container)
使用 ref=“container” 获取 DOM 引用用于计算容器宽度
通过绝对定位的子元素实现瀑布流布局 - 图片项模板(img-item)
通过 v-for 循环渲染处理后的 computedList
动态绑定样式包括:宽度、高度、定位坐标(实现瀑布流的核心) - 图片元素
使用 lazy-load 实现懒加载(需要配合具体实现)
图片加载完成后触发 @load 事件
object-fit: cover 保证图片比例不变形
js代码
<script>
export default {
props: {
list: {
type: Array,
default: () => []
}
},
data() {
return {
containerWidth: 0,
columnHeights: [0, 0], // 左右两列当前高度
computedList: [], // 处理后的数据
}
},
watch: {
list: {
handler(newVal) {
this.initLayout(newVal)
},
immediate: true
}
},
mounted() {
this.getContainerWidth()
window.addEventListener('resize', this.handleResize)
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
},
methods: {
// 获取容器宽度
getContainerWidth() {
this.$nextTick(() => {
this.containerWidth = this.$refs.container.offsetWidth
this.initLayout(this.list)
})
},
// 初始化布局
async initLayout(list) {
this.columnHeights = [0,0]
if (!this.containerWidth) return
const itemWidth = (this.containerWidth - 15) / 2
const gap = 10 // 图片间隔
// 批量计算图片尺寸
const items = await Promise.all(list.map(async (item, index) => {
const imgItem = await this.calcImageSize(item, itemWidth)
return {
...imgItem,
index,
width: itemWidth,
column: -1, // 初始列
top: 0,
left: 0
}
}))
// 重新计算布局
this.computedList = items.reduce((arr, item) => {
const minHeightColumn = this.columnHeights[0] <= this.columnHeights[1] ? 0 : 1
const left = minHeightColumn === 0
? 5
: itemWidth + gap
arr.push({
...item,
left,
top: this.columnHeights[minHeightColumn],
column: minHeightColumn
})
// 更新列高度
this.columnHeights[minHeightColumn] += item.height + gap - 5
return arr
}, [])
},
// 计算图片尺寸
calcImageSize(item, targetWidth) {
return new Promise(resolve => {
const img = new Image()
img.src = item.src
img.onload = () => {
const scale = targetWidth / img.width
resolve({
...item,
height: img.height * scale
})
}
})
},
// 图片加载完成处理
handleImageLoad(index) {
// 可在此添加加载完成后的处理
},
// 窗口resize处理
handleResize: function() {
this.getContainerWidth()
}
}
}
</script>