vue实现简易瀑布流
实现效果
1、不同屏幕尺寸下根据可视区域宽度判断 应该 放几列,这里用onresize演示
2、鼠标滚动到已加载数据的底部时再次请求数据,重新计算哪一列高度最小,push到最小的那一列

实现思路
1、瀑布流的盒子使用flex布局,每一列column的间距相等
2、获取数据,根据可视区域的宽度判断需要几列
3、假设根据判断需要n列,那么将获取到的数据前n个一次push到每一列,成为第一个元素
4、此时每一列都有了元素,找到高度最小的那一列,这里可能有多列,找到索引最小那列,然后将元素插入
5、监听滚动事件,可以是document,也可以是具体的元素,只要元素是包裹着瀑布流的并且overflow不是hidden;当滚动到底部时,请求数据,将获得的数据插入到已有的瀑布流中
具体代码
1、瀑布流的盒子使用flex布局,每一列column的间距相等
<div class="container">
<div class="column" v-for="(column,index) in columns" :key="index">
<div
class="item"
v-for="(item,i) in column.columnArr"
:key="i"
:style="{ height: item.height + 'px', background: item.background }"
>
{{ item.text }}
</div>
</div>
</div>
.container {
margin: 0 auto;
padding-bottom: 20px;
display: flex;
justify-content: space-around;
background: pink;
transition: all 0.5s ease-in-out;
}
.item {
width: 120px;
color: #000;
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.5s ease-in-out;
}
2、获取数据,根据可视区域的宽度判断需要几列
// 根据可视区域的宽度判断需要几列
let cWidth = document.documentElement.clientWidth || document.body.clientWidth;
// 假设图片宽度为140px
let cLen = Math.floor((cWidth/140)-1)
3、假设根据判断需要n列,那么将获取到的数据前n个一次push到每一列,成为第一个元素
// 初始化每一列的第一个元素,this.contentArr为获取到的数据
for (let i = 0; i < cLen; i++) {
this.contentArr[i].top = 0 //预设距离顶部值为0
this.columns.push({columnArr:[this.contentArr[i]]})
}

4、此时每一列都有了元素,找到高度最小的那一列,这里可能有多列,找到索引最小那列,然后将元素插入
// 判断应该放到哪一列
for (var index = cLen; index < contentLen; index++) { this.arrIndex = []
let arr = []
let minHeight = 0
let pushIndex = 0 for (let i = 0; i < this.columns.length; i++) {
arr.push({
height:this.columns[i].columnArr[this.columns[i].columnArr.length-1].height,
top:this.columns[i].columnArr[this.columns[i].columnArr.length-1].top
})
} minHeight = this.getMinHeight(arr) //找到高度最小的一列,可能有多个
this.getMinIndex(minHeight) //高度最小的一列所在位置的索引,用pushIndex存储 if(this.arrIndex.length>0){
pushIndex = Math.min.apply(null,this.arrIndex) //出现高度一样的,去索引最小的
} this.contentArr[index].top = minHeight +20
this.columns[pushIndex].columnArr.push(this.contentArr[index]) }

5、当滚动到底部时,请求数据,将获得的数据插入到已有的瀑布流中
document.onscroll = (e) =>{
let top = e.target.documentElement.scrollTop || e.target.body.scrollTop
let height = e.target.documentElement.scrollHeight || e.target.body.scrollHeight
if((top+clientH)==height){
this.loading = true
this.pushElement().then(() =>{
// 从接口最新获取的元素push到目前高度最小的一列
for (var index = 0; index < this.contentArr2.length; index++) {
this.arrIndex = []
let arr = [] //找到高度最小的一列,可能有多个
let minHeight = 0 //高度最小的一列的高度
let pushIndex = 0 //高度最小的一列所在位置的索引
for (let i = 0; i < this.columns.length; i++) {
arr.push({
height:this.columns[i].columnArr[this.columns[i].columnArr.length-1].height,
top:this.columns[i].columnArr[this.columns[i].columnArr.length-1].top
})
}
minHeight = this.getMinHeight(arr)
this.getMinIndex(minHeight)
if(this.arrIndex.length>0){
pushIndex = Math.min.apply(null,this.arrIndex) //出现高度一样的,去索引最小的
}
this.contentArr[index].top = minHeight +20
this.columns[pushIndex].columnArr.push(this.contentArr[index])
this.loading = false
}
})
}
}
完整代码

<template>
<div style="position:relative;">
<div class="container">
<div class="column" v-for="(column,index) in columns" :key="index">
<div
class="item"
v-for="(item,i) in column.columnArr"
:key="i"
:style="{ height: item.height + 'px', background: item.background }"
>
{{ item.text }}
</div>
</div>
</div>
<div class="loading" v-if="loading" v-loading="loading" element-loading-text="加载中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"></div>
</div>
</template>
<script>
export default {
data() {
return {
contentArr: [
{ value: 0, height: "150", background: "#409eff", text: "1", top: "" },
{ value: 1, height: "250", background: "#67c23a", text: "2", top: "" },
{ value: 2, height: "250", background: "#e6a23c", text: "3", top: "" },
{ value: 3, height: "250", background: "#f56c6c", text: "4", top: "" },
{ value: 4, height: "180", background: "#909399", text: "5", top: "" },
{ value: 5, height: "250", background: "#409eff", text: "6", top: "" },
{ value: 6, height: "180", background: "#67c23a", text: "7", top: "" },
{ value: 7, height: "250", background: "#e6a23c", text: "8", top: "" },
{ value: 8, height: "180", background: "#f56c6c", text: "9", top: "" },
{ value: 9, height: "150", background: "#909399", text: "10", top: "" },
{ value: 10, height: "150", background: "#409eff", text: "11", top: "" },
{ value: 11, height: "250", background: "#67c23a", text: "12", top: "" },
{ value: 12, height: "250", background: "#e6a23c", text: "13", top: "" },
{ value: 13, height: "250", background: "#f56c6c", text: "14", top: "" },
{ value: 14, height: "180", background: "#909399", text: "15", top: "" },
{ value: 15, height: "250", background: "#409eff", text: "16", top: "" },
{ value: 16, height: "180", background: "#67c23a", text: "17", top: "" },
{ value: 17, height: "250", background: "#e6a23c", text: "18", top: "" },
{ value: 18, height: "180", background: "#f56c6c", text: "19", top: "" },
{ value: 19, height: "150", background: "#909399", text: "20", top: "" },
{ value: 20, height: "150", background: "#409eff", text: "21", top: "" },
{ value: 21, height: "250", background: "#67c23a", text: "22", top: "" },
{ value: 22, height: "250", background: "#e6a23c", text: "23", top: "" },
{ value: 23, height: "250", background: "#f56c6c", text: "24", top: "" },
{ value: 24, height: "180", background: "#909399", text: "25", top: "" },
{ value: 25, height: "250", background: "#409eff", text: "26", top: "" },
{ value: 26, height: "180", background: "#67c23a", text: "27", top: "" },
{ value: 27, height: "250", background: "#e6a23c", text: "28", top: "" },
{ value: 28, height: "180", background: "#f56c6c", text: "29", top: "" },
{ value: 29, height: "150", background: "#909399", text: "30", top: "" },
],
columns: [],
arrIndex:[],
loading:false,
contentArr2:[]
};
},
methods: { pushElement(){
return new Promise((resolve,reject) =>{
setTimeout(() =>{
for (let i = 0; i < 20; i++) {
this.contentArr2[i] = { value: i+this.contentArr.length, height: 50*Math.random()+50, background: "#409eff", text: i+this.contentArr.length, top: "" }
}
resolve()
},500)
})
},
getMinHeight(arr){
let a = []
for (let i = 0; i < arr.length; i++) {
a.push(parseInt(arr[i].height)+parseInt(arr[i].top))
}
return Math.min.apply(null,a)
},
getMinIndex(val){
for (let i = 0; i < this.columns.length; i++) {
let height = this.columns[i].columnArr[this.columns[i].columnArr.length-1].height
let top = this.columns[i].columnArr[this.columns[i].columnArr.length-1].top
if(parseInt(height)+parseInt(top)==val){
this.arrIndex.push(i)
}
}
}, init() {
this.columns = []
let contentLen = this.contentArr.length
// 根据可视区域的宽度判断需要几列
let cWidth = document.documentElement.clientWidth || document.body.clientWidth;
// 假设图片宽度为100px
let cLen = Math.floor((cWidth/140)-1)
console.log(cLen); // 初始化每一列的第一行元素
for (let i = 0; i < cLen; i++) {
this.contentArr[i].top = 0 //预设距离顶部值为0
this.columns.push({columnArr:[this.contentArr[i]]})
} // 对剩余元素的判断,应该放到哪一列
for (var index = cLen; index < contentLen; index++) { this.arrIndex = []
let arr = [] //找到高度最小的一列,可能有多个
let minHeight = 0 //高度最小的一列的高度
let pushIndex = 0 //高度最小的一列所在位置的索引 for (let i = 0; i < this.columns.length; i++) {
arr.push({
height:this.columns[i].columnArr[this.columns[i].columnArr.length-1].height,
top:this.columns[i].columnArr[this.columns[i].columnArr.length-1].top
})
} minHeight = this.getMinHeight(arr)
this.getMinIndex(minHeight)
if(this.arrIndex.length>0){
pushIndex = Math.min.apply(null,this.arrIndex) //出现高度一样的,去索引最小的
} this.contentArr[index].top = minHeight +20
this.columns[pushIndex].columnArr.push(this.contentArr[index]) }
},
},
mounted() {
this.init()
window.onresize = () =>{
console.time('aa')
this.init()
console.timeEnd('aa')
}
let clientH = document.documentElement.clientHeight || document.body.clientHeight
document.onscroll = (e) =>{ let top = e.target.documentElement.scrollTop || e.target.body.scrollTop let height = e.target.documentElement.scrollHeight || e.target.body.scrollHeight if((top+clientH)==height){
this.loading = true
this.pushElement().then(() =>{
// 从接口最新获取的元素push到目前高度最小的一列
for (var index = 0; index < this.contentArr2.length; index++) { this.arrIndex = []
let arr = [] //找到高度最小的一列,可能有多个
let minHeight = 0 //高度最小的一列的高度
let pushIndex = 0 //高度最小的一列所在位置的索引 for (let i = 0; i < this.columns.length; i++) {
arr.push({
height:this.columns[i].columnArr[this.columns[i].columnArr.length-1].height,
top:this.columns[i].columnArr[this.columns[i].columnArr.length-1].top
})
} minHeight = this.getMinHeight(arr)
this.getMinIndex(minHeight)
if(this.arrIndex.length>0){
pushIndex = Math.min.apply(null,this.arrIndex) //出现高度一样的,去索引最小的
} this.contentArr[index].top = minHeight +20
this.columns[pushIndex].columnArr.push(this.contentArr[index])
this.loading = false }
})
}
}
},
};
</script> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
div,
p {
margin: 0;
padding: 0;
}
.container {
margin: 0 auto;
padding-bottom: 20px;
display: flex;
justify-content: space-around;
background: pink;
transition: all 0.5s ease-in-out;
}
.item {
width: 120px;
color: #000;
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.5s ease-in-out;
}
.loading{
position:fixed;
top:0;
left:0;
right:0;
bottom:0;
}
</style>
vue实现简易瀑布流的更多相关文章
- 基于jQuery的简易瀑布流实现
<!doctype html> <html> <head> <meta charset="UTF-8"> <title> ...
- vue.js实现瀑布流之vue-waterfall-easy
想必大家应该很多都已经习惯了jquery的DOM操作,jquery的瀑布流实现起来也很容易. 但是,随着时代的发展,随着时代的进步..... 算了算了,扯远了,既然能找到这儿来,肯定是在vue.js上 ...
- vue实现网络图片瀑布流 + 下拉刷新 + 上拉加载更多
一.思路分析和效果图 用vue来实现一个瀑布流效果,加载网络图片,同时有下拉刷新和上拉加载更多功能效果.然后针对这几个效果的实现,捋下思路: 根据加载数据的顺序,依次追加标签展示效果: 选择哪种方式实 ...
- vue 写一个瀑布流插件
效果如图所示: 采用了预先加载图片,再计算高度的办法..网络差的情况下,可能有点卡 新建 vue-water-easy.vue 组件文件 <template> <div class ...
- h5的瀑布流
<!doctype html><html><head><meta charset="utf-8"><title>超简易瀑 ...
- 使用vue做移动端瀑布流分页
讲到瀑布流分页有一个方法一定是要用到的 pullToRefresh() 这个也没什么好解释的,想了解的可以去百度一下 下面上代码 <div id="main" class=& ...
- 自己实现vue瀑布流组件,含详细注释
我知道vue有瀑布流插件vue-waterfall-easy,但是使用的时候与我的预期有部分别,所以就自己动手写了这个组件 人和动物的根本区别是是否会使用工具,我们不仅要会使用,还要会创造工具,别人提 ...
- 用vue.js写的一个瀑布流的组件
用vue.js写的一个瀑布流的组件:https://segmentfault.com/a/1190000010741319 https://www.jianshu.com/p/db3cadc03402
- 分享一个Vue实现图片水平瀑布流的插件
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.需求来源 今天碰到了一个需求,需要在页面里,用水平瀑布流的方式,将一些图片进行加载,这让我突然想起我很久以前写的一篇文章<JS两 ...
- vue 瀑布流实现
<div class="myWrite" v-if="list.length==0"> - 这个福宝有点懒哦 - </div> < ...
随机推荐
- vivo 商城架构升级-SSR 实战篇
一.前言 在前面几篇文章中,相信大家对vivo官网商城的前端架构演变有了一定的了解,从稳步推进前后端分离到小程序多端探索实践,团队不断创新尝试. 在本文中,我们来分享一下vivo官网商城在Node 服 ...
- 理解CAP理论
1. 理论什么是CAP? cap定理,它的提出是对于一个分布式系统得出的一个观点,是不能同时满足下面三点 一致性 可用性 分区容忍性 CAP理论认为,分布式系统最多只能同时满足其中的两个特性,而无法同 ...
- 【调试】kprobes(二)使用方法
前言 上一节介绍了kprobe的基本概念,下面我们将使用几个具体的例子,看下kprobe在实际使用中有那些应用场景. kprobe 内核的samples/kprobe目录下有kprobe相关的例子,我 ...
- webpack-小滴课堂学习笔记
webpack简介 1.webpack是什么 简介:webpack其实就是一个JavaScript应用程序的静态模块打包器. 2.webpack有什么作用 模块化打包:webpack会将项目的资源文件 ...
- 解决navicat连接mysql数据库查询很慢的问题
1.背景: navicat连接数据库进行sql查询,每隔一段时间发现查询会变得很慢 2.原因: Mysql服务器端会定时清理长时间不活跃空闲的数据库连接,进行优化 3.解决方案: Navicat -右 ...
- Deployment 和 StatefulSets 概述
这篇概述是看文章提到的一段话 xxx is not targeted to be horizontally scalable 引发的,遂整理记录在这里. 起因是有两个应用,一个是无状态的,可以 hor ...
- VUEX 使用学习六 : modules
转载请注明出处: 当Store中存放了非常多非常大的共享数据对象时,应用会变的非常的复杂,Store对象也会非常臃肿,所以Vuex提供了一个Module模块来分隔Store.通过对Vuex中的Stor ...
- std::istringstream的用法
1.概要 std::istringstream 是 C++ 标准库中的一个类,它用于从字符串中提取数据,并将数据转换为不同的数据类型.它通常用于从字符串中解析数据,例如整数.浮点数等.以下是关于 st ...
- [转帖]ext4的fsync性能和nodelalloc参数的分析
原文:http://blog.thinksrc.com/?p=189001 感叹归感叹,发泄完了还得继续过. 前几天忙的不可开交,周报上面竟然能列出11项,想想以前在T公司时候的清闲,现在的老板的真幸 ...
- [转帖]grafana 连接 influxdb 1.x 和 2.x
文章目录 一.安装 influxdb Ⅰ.docker 安装 二.常用操作 Ⅰ.influxdb 1.x版本添加用户认证 Ⅱ.influxdb 2.x 使用命令行 Ⅲ.CLI 配置token Ⅴ.CL ...