这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

一.需求来源

今天碰到了一个需求,需要在页面里,用水平瀑布流的方式,将一些图片进行加载,这让我突然想起我很久以前写的一篇文章《JS两种方式实现水平瀑布流布局》

但是有个问题,这个需求是Vue项目的,那没办法,这里给大家分享下我的开发过程,项目主体用的是之前在学习的CRMEB的后端框架来开发,UI使用iView-UI,其余的场景与其他的vue项目一致

二.逻辑设想

如果不是vue环境,我们的逻辑为

1.获取所有的div元素
2.获取盒子的宽度,宽度都是相同,高度不同
3.在浮动布局中每一行的盒子个数不固定,是根据屏幕宽度和盒子宽度决定
4.获取屏幕宽度
5.求出列数,屏幕宽度 / 盒子宽度 取整
6.瀑布流最关键的是第二行的盒子的排布方式,通过获取第一行盒子中最矮的一个的下标,绝对定位,top是最矮盒子的高度,left是最矮盒子的下标 * 盒子的宽度
7.循环遍历所有的盒子,通过列数找到第一行所有的盒子,将第一行盒子的高度放入数组,再取出数组中最小的一个的下标,就是第6步思路的第一行盒子中最矮盒子的下标。
8.循环继续,第二行第一个盒子,通过绝对定位,放进页面。
9.关键,需要将数组中最小的值加上放进的盒子的高度
10.继续循环,遍历所有
11.如果想要加载更多,需要判断最后一个盒子的高度和页面滚动的距离,再将数据通过创建元素,追加进页面,再通过瀑布流布局展示

但如果是Vue项目,我们可以把逻辑归结为以下几步

1.获取屏幕宽度
2..获取盒子的宽度,宽度都是相同,高度不同
3.在浮动布局中每一行的盒子个数不固定,是根据屏幕宽度和盒子宽度决定
4.求出列数,屏幕宽度 / 盒子宽度 取整
5.瀑布流最关键的是第二行的盒子的排布方式,通过获取第一行盒子中最矮的一个的下标,绝对定位,top是最矮盒子的高度,left是最矮盒子的下标 * 盒子的宽度
6.继续循环,遍历所有
7.如果想要加载更多,需要判断最后一个盒子的高度和页面滚动的距离,再将数据通过创建元素,追加进页面,再通过瀑布流布局展示

三.最终效果图片

四.代码分析

先看下我的html部分

<template>
<div class="tab-container" id="tabContainer">
<div class="tab-item" v-for="(item, index) in pbList" :key="index">
<img :src="item.url" />
</div>
</div>
</template> <style scoped>
* {
margin: 0;
padding: 0;
}
/* 最外层大盒子 */
.tab-container {
padding-top: 20px;
position: relative;
}
/* 每个小盒子 */
.tab-container .tab-item {
position: absolute;
height: auto;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
background: white;
/* 元素不能中断显示 */
break-inside: avoid;
text-align: center;
}
.tab-container .tab-item img {
width: 100%;
height: auto;
display: block;
}
</style>

核心js部分

<script>
export default {
name:'compList',
props:{
pbList:{
type:Array,
default:()=>{return []}
}
},
data() {
return {
};
},
mounted() {
this.$nextTick(()=>{
this.waterFall("#tabContainer", ".tab-item"); //实现瀑布流
})
},
methods: {
waterFall(
wrapIdName,
contentIdName,
columns = 5,
columnGap = 20,
rowGap = 20
) {
// 获得内容可用宽度(去除滚动条宽度)
const wrapContentWidth =
document.querySelector(wrapIdName).offsetWidth; // 间隔空白区域
const whiteArea = (columns - 1) * columnGap; // 得到每列宽度(也即每项内容宽度)
const contentWidth = parseInt((wrapContentWidth - whiteArea) / columns); // 得到内容项集合
const contentList = document.querySelectorAll(contentIdName); // 成行内容项高度集合
const lineConentHeightList = []; for (let i = 0; i < contentList.length; i++) {
// 动态设置内容项宽度
contentList[i].style.width = contentWidth + "px"; // 获取内容项高度
const height = contentList[i].clientHeight; if (i < columns) {
// 第一行按序布局
contentList[i].style.top = "0px";
contentList[i].style.left = contentWidth * i + columnGap * i + "px"; // 将行高push到数组
lineConentHeightList.push(height);
} 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;
}
}
},
},
};
</script>

这里要给大家提个醒,在当插件使用的时候,我们需要用this.$nextTick()来进行页面初始化,因为方法成功的前提是要等页面初始化加载完毕后才能进行获取和计算

总体插件代码为:

<template>
<div class="tab-container" id="tabContainer">
<div class="tab-item" v-for="(item, index) in pbList" :key="index">
<img :src="item.url" />
</div>
</div>
</template> <script>
export default {
name:'compList',
props:{
pbList:{
type:Array,
default:()=>{return []}
}
},
data() {
return {
};
},
mounted() {
this.$nextTick(()=>{
this.waterFall("#tabContainer", ".tab-item"); //实现瀑布流
})
},
methods: {
waterFall(
wrapIdName,
contentIdName,
columns = 5,
columnGap = 20,
rowGap = 20
) {
// 获得内容可用宽度(去除滚动条宽度)
const wrapContentWidth =
document.querySelector(wrapIdName).offsetWidth; // 间隔空白区域
const whiteArea = (columns - 1) * columnGap; // 得到每列宽度(也即每项内容宽度)
const contentWidth = parseInt((wrapContentWidth - whiteArea) / columns); // 得到内容项集合
const contentList = document.querySelectorAll(contentIdName); // 成行内容项高度集合
const lineConentHeightList = []; for (let i = 0; i < contentList.length; i++) {
// 动态设置内容项宽度
contentList[i].style.width = contentWidth + "px"; // 获取内容项高度
const height = contentList[i].clientHeight; if (i < columns) {
// 第一行按序布局
contentList[i].style.top = "0px";
contentList[i].style.left = contentWidth * i + columnGap * i + "px"; // 将行高push到数组
lineConentHeightList.push(height);
} 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;
}
}
},
},
};
</script> <style scoped>
* {
margin: 0;
padding: 0;
}
/* 最外层大盒子 */
.tab-container {
padding-top: 20px;
position: relative;
}
/* 每个小盒子 */
.tab-container .tab-item {
position: absolute;
height: auto;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
background: white;
/* 元素不能中断显示 */
break-inside: avoid;
text-align: center;
}
.tab-container .tab-item img {
width: 100%;
height: auto;
display: block;
}
</style>

五.外层使用和懒加载

在使用这个插件的时候,有两个问题,就是因为内层是position: absolute;定位,不会撑开外部的div,会导致外层盒模型不好布局,还有就是页面下拉懒加载,那要怎么办呢?

这里我给出我的处理方法

整体代码如下:

<template>
<div>
<div class="list-box" @scroll="scrollFun">
<compList :pbList="pbList" ref="compList"></compList>
</div>
</div>
</template> <script>
import compList from "@/pages/test/components/compList";
export default {
name:'testList',
components:{
compList
},
data() {
return {
//瀑布流数据
pbList: [
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
}
],
addList:[
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
},
{
url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
}
],
bottomMain:true
};
},
methods:{
scrollFun(e) {
const offsetHeight= e.target.offsetHeight
const scrollHeight= e.target.scrollHeight
const scrollTop= e.target.scrollTop
if((scrollHeight - (offsetHeight+scrollTop)) < 10){
if(this.bottomMain){
this.bottomMain = false
this.addListDataFun()
}
}
},
addListDataFun(){
this.$Spin.show({
render: (h) => {
return h('div', [
h('Icon', {
'class': 'demo-spin-icon-load',
props: {
type: 'ios-loading',
size: 18
}
}),
h('div', '数据更新中...')
])
}
});
setTimeout(() => {
this.pbList = this.pbList.concat(this.addList)
this.bottomMain = true
this.$nextTick(()=>{
this.$refs.compList.waterFall("#tabContainer", ".tab-item")
this.$Spin.hide()
})
},1000)
}
}
};
</script> <style scoped>
.list-box{
position: relative;
width: 100%;
height: calc(100vh - 240px);
background: white;
padding: 20px 30px 20px 20px;
margin-top: 20px;
box-sizing: border-box;
overflow: auto;
}
</style>

下拉的核心代码为:

    scrollFun(e) {
const offsetHeight= e.target.offsetHeight
const scrollHeight= e.target.scrollHeight
const scrollTop= e.target.scrollTop
if((scrollHeight - (offsetHeight+scrollTop)) < 10){
if(this.bottomMain){
this.bottomMain = false
this.addListDataFun()
}
}
},
addListDataFun(){
this.$Spin.show({
render: (h) => {
return h('div', [
h('Icon', {
'class': 'demo-spin-icon-load',
props: {
type: 'ios-loading',
size: 18
}
}),
h('div', '数据更新中...')
])
}
});
setTimeout(() => {
this.pbList = this.pbList.concat(this.addList)
this.bottomMain = true
this.$nextTick(()=>{
this.$refs.compList.waterFall("#tabContainer", ".tab-item")
this.$Spin.hide()
})
},1000)
}

这里使用的是iView-UI的全局加载事件,如果你要用其他的UI框架,也可以自行修改

到这里,所有的思路就结束了

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

分享一个Vue实现图片水平瀑布流的插件的更多相关文章

  1. 使用vue做移动端瀑布流分页

    讲到瀑布流分页有一个方法一定是要用到的 pullToRefresh() 这个也没什么好解释的,想了解的可以去百度一下 下面上代码 <div id="main" class=& ...

  2. 使用JS实现图片展示瀑布流效果

    不知大家有没有发现,一般的图片展示网站都会使用瀑布流效果,所谓的瀑布流 就是网站内的图片不会一下子全缓存出来,而是等你滚动到一定的距离的时候, 下面的图片才会继续缓存,并且图片也是随机出现的,只是宽度 ...

  3. js实现图片的瀑布流

    先看效果: 初始状态:

  4. StaggeredGridView+universal-image-loader载入网路图片实现瀑布流

    StaggeredGridView 开源lib  https://github.com/maurycyw/StaggeredGridView 文章demo下载地址  http://download.c ...

  5. 分享一个vue项目“脚手架”项目的实现步骤

    搭建缘由 源于公司每次新启动一个由多人协同开发的项目都由负责人初始化项目之后,每个人再去从私服pull一下项目才开始开发.但是每次初始化工程都是一步步的造轮子,一个个依赖去安装,新建一个个不同功能的文 ...

  6. 实现一个vue的图片预览插件

    vue-image-swipe 基于photoswipe实现的vue图片预览组件 安装 1 第一步 npm install vue-image-swipe -D 2 第二步 vue 入口文件引入 im ...

  7. 分享一个上传图片,图片压缩Unsupported Image Type解决方案

    http://blog.csdn.net/frankcheng5143/article/details/53185201 *************************************** ...

  8. 分享一个Vue数组赋值的错误

    今天在写项目用到Vue的时候,遇到的一个问题,纠结了好一会,首先我的代码是这样的 有没有毛病!!  开始我感觉是没啥毛病啊,按照之前写Java代码的逻辑,我感觉这没一点毛病 . 但是它就是有毛病, 假 ...

  9. 分享一个vue常用的ui控件

      vue学习文档 http://www.jianshu.com/p/8a272fc4e8e8 vux github ui demo:https://github.com/airyland/vux M ...

随机推荐

  1. esp8266模拟输入(ADC)检测问题

    今天使用esp12f读取A0数据时一直出现错误; Serial.println(analogRead(A0));读取值一直为1024 因为前段时间一直用的是开发板,读取电压值正常 而从昨天换为了esp ...

  2. typescript中的esModuleInterop选项

    当没有加esModuleInterop时 库的代码: export const a = 1; export default function b() {} 生成代码 exports.__esModul ...

  3. 8000字讲透OBSA原理与应用实践

    摘要:OBSA项目是围绕OBS建立的大数据和AI生态,其在不断的发展和完善中,目前有如下子项目:hadoop-obs项目和flink-obs项目. 文章作者:存储服务产品部开发者支持团队 OBS存储服 ...

  4. github访问较慢问题初步解决方案

    简介 众所周知,github在国内向来都是访问的非常坎坷,不挂代理访问起来非常困难,本文将介绍一种不依赖代理的加快访问的方式,但不一定可行哦,只能说是优化 步骤 1.在搜索框中输入记事本或者notep ...

  5. 合并表格行---三层for循环遍历数据

    合并表格行---三层for循环遍历数据 示例1 json <!DOCTYPE html> <html lang="zh_cn"> <head> ...

  6. BACnet MS/TP转MQTT网关金鸽BL103

    BACnet MS/TP转MQTT网关金鸽BL103BL103是一款BACnet路由器,实现 BACnet MS/TP 总线和以太网 BACnetIP 之间通信路由功能,同时也是一款Modbus RT ...

  7. 使用python3.7配置开发钉钉群自定义机器人(2020年新版攻略)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_132 最近疫情比较严重,很多公司依靠阿里旗下的办公软件钉钉来进行远程办公,当然了,钉钉这个产品真的是让人一言难尽,要多难用有多难用 ...

  8. 关于hive分区,你知道多少呢?

    ​ 文末查看关键字,回复赠书 一.理论基础 1.Hive分区背景 在Hive Select查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作.有时候只需要扫描表中关心的一部分数据,因此建表时引入 ...

  9. Linux系列之压缩命令

    前言 Linux 有三个常用的压缩命令:gzip.bzip2和 tar .本文介绍它们的区别和简单用法. 压缩文件 数据压缩是去除数据中冗余部分的过程,需要通过压缩算法完成.这些算法可分为两类: 无损 ...

  10. Synchronized锁升级原理与过程深入剖析

    Synchronized锁升级原理与过程深入剖析 前言 在上篇文章深入学习Synchronized各种使用方法当中我们仔细介绍了在各种情况下该如何使用synchronized关键字.因为在我们写的程序 ...