移动端无规则瀑布流实现
·
瀑布流在移动端的应用及发展趋势
瀑布流布局在移动端设计中因其灵活性和高效性而被广泛采用。这种布局方式能够动态调整内容展示,适应不同屏幕尺寸,提升用户体验。
移动端瀑布流的设计特点
瀑布流设计在移动端表现出强大的适应性,能够根据用户行为实时加载内容。高度不一的元素排列方式节省了空间,同时增强了视觉吸引力。
技术实现的关键要素
实现高效瀑布流需要考虑性能优化和动态加载策略。通过懒加载和分页技术减少初始加载时间,确保流畅的用户体验。响应式设计确保在不同设备上保持一致的表现。
未来发展方向
随着移动设备性能的提升,瀑布流将进一步结合AI和个性化推荐算法。动态内容调整和交互式元素的加入将丰富用户体验,使其在移动端应用中持续占据重要地位。
主要实现动态列数的瀑布流可以自定义列数
瀑布流布局实现方法
该方法通过动态计算每个内容项的位置,实现自适应宽度的瀑布流布局。适用于图片、卡片等内容的展示。
方法参数说明
/**
* @param { string } wrapIdName 容器id(或class)名称
* @param { string } contentIdName 容器中内容项id(或class)名称
* @param { number } column 容器中内容展示列数
* @param { number } columnGap 列间隔距离
* @param { number } rowGap 行间隔距离
*/
核心实现逻辑
计算容器可用宽度和每项宽度
let wrapContentWidth = document.getElementById('tabContainer').offsetWidth - 8
let whiteArea = (columns - 1) * columnGap
let contentWidth = parseInt((wrapContentWidth - whiteArea) / columns)
处理第一行元素的定位
contentList[i].style.top = 0
contentList[i].style.left = contentWidth * i + columnGap * i + "px"
lineConentHeightList.push(height)
处理后续行元素的定位(寻找最短列)
let minHeight = Math.min(...lineConentHeightList)
let index = lineConentHeightList.findIndex(listH => listH === minHeight)
contentList[i].style.top = minHeight + rowGap + "px"
contentList[i].style.left = (contentWidth + columnGap) * index + "px"
lineConentHeightList[index] += height + rowGap
使用示例
// 2列布局,间隔16px
waterFall('#container', '.item', 2, 16, 16)
// 3列布局,间隔20px
waterFall('#gallery', '.photo', 3, 20, 20)
注意事项
- 容器需要设置相对定位(position: relative)
- 内容项需要设置绝对定位(position: absolute)
- 在窗口resize时需要重新调用该方法
- 新增内容时需要重新计算布局
响应式适配建议
可通过监听窗口变化动态调整列数:
window.addEventListener('resize', () => {
const cols = window.innerWidth < 768 ? 2 : 4
waterFall('#container', '.item', cols)
})
主要实现瀑布流的方法
/**
* @param { string } wrapIdName 容器id(或class)名称
* @param { string } contentIdName 容器中内容项id(或class)名称
* @param { number } column 容器中内容展示列数 手机的话建议改为2
* @param { number } columnGap 容器中 列 间隔距离 默认为20
* @param { number } rowGap 容器中 行 间隔距离 默认为20
*/
//瀑布流方法:通过拿到dom循环,给每一个dom添加对应的定位位置排列出瀑布流布局。
//通过判断列的高度,来把下一个盒子放在最短的地方补上
waterFall(
wrapIdName,
contentIdName,
columns = 2,
columnGap = 16,
rowGap = 16
) {
// 获得内容可用宽度(去除滚动条宽度)
// let wrapContentWidth = document.querySelector(wrapIdName).offsetWidth - 8;
let wrapContentWidth = document.getElementById('tabContainer').offsetWidth - 8
// console.log(wrapContentWidth,'wrapContenstWidth');
// 间隔空白区域
let whiteArea = (columns - 1) * columnGap;
// 得到每列宽度(也即每项内容宽度)
let contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
// 得到内容项集合
let contentList = document.querySelectorAll(contentIdName);
// 成行内容项高度集合
let lineConentHeightList = [];
for (let i = 0; i < contentList.length; i++) {
// 动态设置内容项宽度
contentList[i].style.width = contentWidth + "px";
// 获取内容项高度
let height = contentList[i].clientHeight;
if (i < columns) {
// 第一行按序布局
contentList[i].style.top = 0;
contentList[i].style.left = contentWidth * i + columnGap * i + "px";
// 将行高push到数组
lineConentHeightList.push(height);
document.getElementById("ttrs").style.height = lineConentHeightList[0] + "px"
} else {
// 其他行
// 获取数组最小的高度 和 对应索引
let minHeight = Math.min(...lineConentHeightList);
let index = lineConentHeightList.findIndex(
listH => listH === minHeight
);
contentList[i].style.top = minHeight + rowGap + "px";
contentList[i].style.left = (contentWidth + columnGap) * index + "px";
// 修改最小列的高度 最小列的高度 = 当前自己的高度 + 拼接过来的高度 + 行间距
lineConentHeightList[index] += height + rowGap;
}
}
//拿到最大高度赋值给页面
let max = Math.max.apply(null, lineConentHeightList)
document.getElementById("ttrs").style.height = max + "px"
},
}
附上所有源码,若有任何改进建议或合作想法,随时可以联系。共同学习,一起进步!
<template>
<div id="ttrs" class="ttr">
<div class="tab-container" id="tabContainer">
<div class="tab-item" v-for="(item, index) in pbList" :key="index" @click="handleJumpDetail(item)">
<img :src="Url" class="water_img">
</div>
</div>
</div>
</template>
<script>
export default {
props: {
datalist: {
type: Array,
default: []
},
//图片数组长度
commelength: {
type:Number,
default: 0,
}
},
data() {
return {
//瀑布流数据
pbList: [],
newLength:0,//新的图片长度
nowImgComLen:0,//加载完成的图片长度
};
},
watch: {
datalist: {
deep: true,
handler(newV,oldV) {
this.newLength = newV.length;
if(newV.length != 0){
this.handleMountedImg();
}
this.pbList = newV
}
}
},
created() {
},
mounted() {
this.pbList = [...this.datalist];
this.newLength = this.pbList.length;
this.handleMountedImg();
},
methods: {
handleMountedImg() {
this.$nextTick(() => {
let imgLen = this.newLength - this.commelength
for (let i = 0; i < imgLen; i++) {
let imgList = document.querySelector(".water_img");
imgList.onload = this.handlescroll();
}
})
},
handlescroll() {
this.waterFall("#tabContainer", ".tab-item"); //实现瀑布流
// 窗口变化自适应布局
window.onresize = () => {
return (() => {
this.waterFall("#tabContainer", ".tab-item");
})();
};
},
/**
* @param { string } wrapIdName 容器id(或class)名称
* @param { string } contentIdName 容器中内容项id(或class)名称
* @param { number } column 容器中内容展示列数 手机的话建议改为2
* @param { number } columnGap 容器中 列 间隔距离 默认为20
* @param { number } rowGap 容器中 行 间隔距离 默认为20
*/
//瀑布流方法:通过拿到dom循环,给每一个dom添加对应的定位位置排列出瀑布流布局。
//通过判断列的高度,来把下一个盒子放在最短的地方补上
waterFall(
wrapIdName,
contentIdName,
columns = 2,
columnGap = 16,
rowGap = 16
) {
// 获得内容可用宽度(去除滚动条宽度)
// let wrapContentWidth = document.querySelector(wrapIdName).offsetWidth - 8;
let wrapContentWidth = document.getElementById('tabContainer').offsetWidth - 8
// console.log(wrapContentWidth,'wrapContenstWidth');
// 间隔空白区域
let whiteArea = (columns - 1) * columnGap;
// 得到每列宽度(也即每项内容宽度)
let contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
// 得到内容项集合
let contentList = document.querySelectorAll(contentIdName);
// 成行内容项高度集合
let lineConentHeightList = [];
for (let i = 0; i < contentList.length; i++) {
// 动态设置内容项宽度
contentList[i].style.width = contentWidth + "px";
// 获取内容项高度
let height = contentList[i].clientHeight;
if (i < columns) {
// 第一行按序布局
contentList[i].style.top = 0;
contentList[i].style.left = contentWidth * i + columnGap * i + "px";
// 将行高push到数组
lineConentHeightList.push(height);
document.getElementById("ttrs").style.height = lineConentHeightList[0] + "px"
} else {
// 其他行
// 获取数组最小的高度 和 对应索引
let minHeight = Math.min(...lineConentHeightList);
let index = lineConentHeightList.findIndex(
listH => listH === minHeight
);
contentList[i].style.top = minHeight + rowGap + "px";
contentList[i].style.left = (contentWidth + columnGap) * index + "px";
// 修改最小列的高度 最小列的高度 = 当前自己的高度 + 拼接过来的高度 + 行间距
lineConentHeightList[index] += height + rowGap;
}
}
//拿到最大高度赋值给页面
let max = Math.max.apply(null, lineConentHeightList)
document.getElementById("ttrs").style.height = max + "px"
},
}
};
</script>
<style lang="less" scoped>
.ttr {
width: 100%;
}
.tab-container {
width: 100%;
background-color: #fff;
height: 100%;
position: relative;
.tab-item {
margin-bottom: 16px;
position: absolute;
height: auto;
border-radius: 20px;
border: 1px solid #e5e6eb;
background: #fff;
/* 元素不能中断显示 */
break-inside: avoid;
img {
max-width: 320px;
width: 100%;
height: auto;
border-radius: 12px;
}
}
}
</style>
更多推荐

所有评论(0)