简介:商城瀑布流,商品的图片尺寸不同,实现高低错落排列的感觉。
原理:图片尺寸不一样,让宽度保持一致,高度自适应,所以后端返回了图片的原始宽高,方便计算。
分成2列(可自定义),接口拿到数据,先计算出图片所占高度,在根据当前高度计算出两列中哪列矮,将数据push到哪一列。
效果:
代码:vue3组合式api+ts+vant
<template>
<div class="all_wrap">
<div class="inner">
<van-list
v-model:loading="state.loading"
v-model:error="state.error"
:finished="state.finished"
error-text="请求失败,点击重新加载"
finished-text="没有更多了"
@load="onLoad"
>
<div class="list_inner">
<div class="lists_more" v-for="(ele, i) of waterfallList" :key="i">
<div
class="list_item"
v-for="(item, index) in ele"
:key="index"
>
<!-- 自定义瀑布流内容 start -->
<img
v-if="item.showImage"
:style="{ height: item.height + 'px' }"
:src="item.showImage.split(',')[0]"
alt=""
/>
<div
class="else_picture"
:style="{ height: item.height + 'px' }"
v-else
></div>
<div class="list_detail">
<div class="list_name">{{ item.name }}</div>
<div class="list_pos">地点:{{ item.address }}</div>
</div>
<!-- 自定义瀑布流内容 end -->
</div>
</div>
</div>
</van-list>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, onMounted, nextTick, ref } from "vue";
import {
attractionsRecommended,
} from "@/api/common";
const wrapWidth = ref(0); // 瀑布的wrap的宽度
onMounted(async () => {
// 获取当前设备宽度
wrapWidth.value = document.querySelector(".list_inner").clientWidth;
});
// 最新推荐
const state = reactive({
loading: true,
error: false, // 加载失败
finished: false,
});
const pages = reactive({
pageNo: 1,
pageSize: 6,
total: 0,
}); // 分页信息
const waterfallList = ref([[], []]); // 瀑布数据,分2列(几列就几个空数组)
const onLoad = async () => {
state.loading = true;
state.error = false; // 请求失败关闭
const data = {
pageNo: pages.pageNo,
pageSize: pages.pageSize,
};
const res: any = await attractionsRecommended(data); // 请求接口
state.loading = false;
if (res && res.code === 200) {
res.data.forEach(async (item) => {
// 获取图片宽高信息
let temp = item.showImage.split("?")[1];
if (temp) {
temp = temp.split("&");
const _picWidth = wrapWidth.value * 0.49; // 自定义宽度
// 换算比例
const _H = ((_picWidth / temp[0].split("=")[1]) * temp[1].split("=")[1]).toFixed(2);
item.height = _H;
}
// 将数据push
nextTick(() => {
// 不能去掉定时器,需要异步延时,不然一下子全部push进去,就跑到一列上了
setTimeout(() => {
const DOMS = document.querySelectorAll(".lists_more");
if (DOMS[0].clientHeight <= DOMS[1].clientHeight) {
waterfallList.value[0].push(item);
} else {
waterfallList.value[1].push(item);
}
}, 0);
});
});
// 分页等信息
pages.total = res.total;
const temp = pages.pageNo * pages.pageSize;
if (pages.total > temp) {
pages.pageNo++;
state.finished = false;
} else {
state.finished = true;
}
} else {
state.error = true; // 展示请求失败
}
};
// 加载列表
</script>
<style lang="scss" scoped>
.all_wrap {
width: 100%;
height: 100%;
background: #f6f7f8;
position: relative;
font-size: 16px;
box-sizing: border-box;
font-family: PingFangSC-Medium, PingFang SC;
overflow: hidden;
.inner {
width: 100%;
height: 100%;
overflow: auto;
padding: 12px;
&::-webkit-scrollbar {
display: none !important;
}
box-sizing: border-box;
}
// 瀑布流样式
.list_inner {
width: 100%;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.lists_more {
width: 49%;
height: min-content;
.list_item {
margin-bottom: 6px;
border-radius: 4px;
overflow: hidden;
img {
width: 100%;
min-height: 100px;
max-height: 300px;
display: block;
}
.list_detail {
padding: 6px 12px;
background: #fff;
.list_name,
.list_pos {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
text-overflow: -webkit-ellipsis-lastline;
display: -webkit-box;
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
.list_name {
font-size: 14px;
font-weight: 600;
color: #333333;
line-height: 20px;
margin-bottom: 4px;
}
.list_pos {
font-size: 12px;
color: #666666;
line-height: 17px;
}
}
}
}
}
}
</style>