我知道vue有瀑布流插件vue-waterfall-easy,但是使用的时候与我的预期有部分别,所以就自己动手写了这个组件

人和动物的根本区别是是否会使用工具,我们不仅要会使用,还要会创造工具,别人提供的工具不一定能满足自己的需求。

先来张效果图:

使用示例:


html:

    <" :onReachbottom="onReachBottom">

      //插槽内容,根据个人需求对数据进行渲染,数据为goods,建议使用组件,方便设置样式

      //这里根据我自己的需求,使用自己的goodsInfo组件对数据goods进行了渲染

      <goodsInfo slot-scope="{goods}" :goods="goods"/>

    </waterfall>

  


JS:

  methods:{

     onReachbottom(){

        //此方法用于数据请求,返回一个Promise,需要以属性方式传入组件,

        //示例:

        return Axios.post('http://xxx.xxx.xx:8088/getData',qs.stringify({

          pageSize:8,

          page:1

        })

      }

    } 

  参数:

    col:必传,瀑布流分为多少列

    onReachBottom: 必传,新数据得获取方式


    

思想:

  1. 在组件挂载时,根据用户传入的 col(多少列),初始化渲染列表为一个二维数组

  2. 组件挂载时发送首次数据请求,并进行首次渲染(如果你喜欢,也可以在created时发送请求)

  3.  需要根据列的高度来判断每条数据填充至哪一列(此处有坑),每次填充都需要先更新视图再进入下一条数据得判断,否则每次获取到的高度都是一致的,这样会导致所有数据填充至一列(由于使用二维数组,导致这个坑被我踩烂了)

  4. 添加滚动事件,判断触底,并再次拉取数据

 


 

实现代码:

 HTML:

    

<template>
  <div class="myWaterfall" v-show="imgsArr" ref="fallbox">
  <ul>
    <!--列的宽度应由计算的来-->
    <li v-for="(it,index) in col" :style="{width:(100/col-1)+'%'}" :ref="'col'+index" :key="it">
    <div v-for="goods in renderList[index]" :key="goods.ID">
       //插槽,用户如何对数据渲染
      <slot :goods="goods" />
    </div>
    </li>
  </ul>
  <div class="loading" v-show="rendering">
  <div class="wait">
    <span>L</span>
    <span>o</span>
    <span>a</span>
    <span>d</span>
    <span>i</span>
    <span>n</span>
    <span>g</span>
    ...
  </div>
  </div>
  <p class="nomore" v-if="over">没有更多了>>></p>
</div>
</template>
 

Script:
  

export default {
  props: ["col","onReachBottom"],
  data(){
    return{
      loading : false ,     //是否处于加载状态,用于触底事件的节流
      nowPage : 1,
      imgsArr : [],      //数据列表
      over : false,      //是否已经到底了
      lock : false,      //请求锁
      rendering : false,   //渲染中,防止渲染未完成高度获取不准确而导致可连续触发请求
      renderList : [],     //渲染列表,根据imgsArr+col初始化为二维数组
      }
    },
  methods: {
    computedOffset(obj,prop){   //工具函数,计算元素到body的绝对位置,获取obj元素的prop值(prop为offset中的某一项)
      if(obj==document.body || obj.offsetParent == document.body){
        return parseInt(obj[prop])
      }
      return parseInt(obj[prop]) + this.computedOffset(obj.offsetParent,prop)    //递归
      },
 
    getDataList() {                 //数据加载
                          //节流处理
      if(!this.loading && !this.over){      //不处于加载状态且有新数据
       let self = this;
       this.loading = true;
                          //页数增加
        this.onReachBottom(self.nowPage).then(res=>{
                          //拼接
          self.rendering = true;      //渲染状态中
          if(res.data.list.length>0){
            self.nowPage++;
            let len = self.imgsArr.length;
            self.imgsArr= self.imgsArr.concat(res.data.list);
            self.fullData(len);      //仅对新的数据做渲染,需要从原数组的终点开始
            self.lock=false
          }else{
            //没有新数据
            self.over = true;
            self.rendering = false;
            }
         self.loading = false;
        })
      }else{
                  //处于请求状态,节流,或已出现无数据(over),忽略请求
        return false;
      }
    },
    
   fullData(index){        //比较列的高度来判断向哪一个列中添加数据
    if(index < this.imgsArr.length){
    let self = this;
    let newImg = new Image();
    newImg.src = self.imgsArr[index].img
    newImg.onload=()=>{     //需等待图片加载,否则高度不准确
    let colHeightList = [];      //所有列的高度表
    for(let i = 0 ; i < self.col ; i++){
    colHeightList[i] = self.$refs['col' + i][0].offsetHeight;
   }
          //获取最小列
    let min = colHeightList.indexOf(Math.min.apply(Math, colHeightList));
    // self.renderList[min].push(self.imgsArr[index]);    踩坑
    let tar = self.renderList[min].concat(self.imgsArr[index])
          //需要更新视图,上面的使用push不会更新视图(操作的第二维),使用set
    self.$set(self.renderList,min,tar)
    self.fullData(index+1)
   }
  }else{
    this.rendering = false;
  }

  

 }
},
  mounted(){
     let self =this;
 
   //渲染列 列表,根据如的col生成对应列数,并置为空的二维数组
    for(let i=0;i<this.col;i++){
      this.renderList[i] = []
    }
 
   //请求首次数据:
    this.getDataList();
 
 
 
   //监听滚动事件
    window.onscroll=function(e) {
      //监测触底
      //瀑布流高度 + 瀑布流的相对top < 可视区高度+滚动距离 ==触底
      //获取到瀑布流盒子
      let box = self.$refs.fallbox;
        //获取到盒子相对于文档的位置
      let top = self.computedOffset(box, 'offsetTop');
      let height = box.offsetHeight;
        //可视区高度
      let clientHeight = document.documentElement.clientHeight;
        //滚动距离
      let scrollTop = document.documentElement.scrollTop;
      if (top + height < clientHeight + scrollTop + 50 && !self.lock && !self.rendering) {
        //触底判断,50用于提前触发,不用完全到底才触发
        //触底成功
        self.lock=true;
        self.getDataList();
       }
      }
    this.fullData(0);
  },
  beforeDestroy(){
    //取消滚动事件,重要,否则路由跳转后执行scroll事件将会有一堆的undefined
    window.onscroll=null;
    //滚动条置顶,否则路由跳转后滚动条的位置没有变化
    document.documentElement.scrollTop=0;
  }
}
 

Css:使用了less语法
  

<style lang="less" >
@keyframes jump{
0%{
top:-10px;
}
100%{
top:10px;
}
}
.loading{
position:fixed;
width:100%;
height:100%;
background:rgba(0,0,0,.2);
top:0;
left:0;
.wait{
font-size:14px;
background:rgba(0,0,0,.8);
border-radius:10px;
line-height:50px;
font-weight:900;
width:200px;
height:50px;
position:absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
letter-spacing: 2px;
span{
font-size:20px;
position:relative;
}
span:first-of-type{
color:red;
animation: jump 0.8s linear alternate-reverse infinite
}
span:nth-of-type(2){
color:orange;
animation: jump .8s linear 0.3s alternate-reverse infinite
}
span:nth-of-type(3){
color:yellow;
animation: jump .8s linear .6s alternate-reverse infinite
}
span:nth-of-type(4){
color:green;
animation: jump .8s linear .9s alternate-reverse infinite
}
span:nth-of-type(5){
color:cyan;
animation: jump .8s linear 1.2s alternate-reverse infinite
}
span:nth-of-type(6){
color:blue;
animation: jump .8s linear 1.5s alternate-reverse infinite
}
span:nth-of-type(7){
color:purple;
animation: jump .8s linear 1.8s alternate-reverse infinite
}
}
}
.myWaterfall {
width: 100%;
height: 100%;
.nomore{
color:grey;
height:30px;
line-height:30px;
}
ul {
/*display: flex;*/
overflow: hidden;
padding:10px;
background:whitesmoke;
border-radius:10px;
li {
/*overflow: hidden;*/
/*flex: 1;*/
float:left;
/*width:25%;*/
margin: 0 5px;
color: #444;
overflow:hidden;
.goodsA:hover {
color: darkorange;
background-color: rgba(223,234,200,.1);
}
.goodsA{
width:100%;
cursor: pointer;
box-sizing:border-box;
border:1px solid #ddd;
box-shadow:3px 1px 3px 0 grey;
background:red;
margin-bottom: 20px;
background-color: #fff;
img {
width: 100%;
}
.goodsInfo {
width: 100%;
.goodsName {
font-weight: 900;
font-size: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height:40px;
line-height:40px;
text-indent:10px;
}
.description {
font-size: 12px;
text-align: left;
text-indent: 10px;
/*height:25px;*/
line-height:25px;
}
.price{
height:30px;
line-height:30px;
font-size:10px;
text-align: left;
text-indent:10px;
position: relative;
.nowPrice{
color:rgb(253, 132, 18);
font-weight:900;
font-size:18px;
margin-right:10px;
em{
font-size:24px;
}
}
.originPrice{
font-size:14px;
text-decoration: line-through;
color:#999;
}
.sale{
position: absolute;
right:5px;
top:5px;
color:#444;
}
}
}
}
}
}
}
</style>
 
以上代码就是我用来实现瀑布流组件的,并没有什么高深的用法,只是提供这样的思路,希望对你自己实现瀑布流有所启发!
copyRight by 埃尔本  2019-09-03 
 
组件(copy)地址:

自己实现vue瀑布流组件,含详细注释的更多相关文章

  1. vuejs和webpack项目(VueComponent)初尝试——瀑布流组件

    碎碎念:     好久不见,最近自己有些懈怠没更过多少博,主要原因之一是对自己学习方式的一些思考,翻看之前的博客多是记录学习笔记这反映出了自己对于前端还停留在学习-复习知识点的阶段压根没多少实践经验啊 ...

  2. vue2.0的瀑布流组件-使用说明

    做一个小项目,需要瀑布流,就选他了,先看看效果 使用瀑布流布局组件:vue-waterfall-easy 下载引入: 方式一:直接从git上复制组件的完整代码,引入vue组件文件即可 import v ...

  3. Vue自己写组件——Demo详细步骤

    公司近期发力,同时开了四五个大项目,并且都是用Vue来做的,我很荣幸的被分到了写项目公用模块的组,所以需要将公用的部分提取成组件的形式,供几个项目共同使用,下面详细讲一下写Vue组件的具体步骤. 一. ...

  4. vue 瀑布流实现

    <div class="myWrite" v-if="list.length==0"> - 这个福宝有点懒哦 - </div> < ...

  5. vue-waterfall2 基于Vue.js 瀑布流组件

    vue-waterfall2 1.宽度自适应,数据绑定特效(适用于上拉加载更多) 2.自定义程度高 3.使用极为简便,适用于PC/移动端 4.提供resize(强制刷新布局-适用于下拉刷新)/mix( ...

  6. 《前端面试加分项目》系列 企业级Vue瀑布流

    本文 GitHub github.com/ponkans/F2E 已收录,有一线大厂面试点思维导图,也整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习.文末有福利~~ 前言 接水怪又来 ...

  7. vue.js实现瀑布流之vue-waterfall-easy

    想必大家应该很多都已经习惯了jquery的DOM操作,jquery的瀑布流实现起来也很容易. 但是,随着时代的发展,随着时代的进步..... 算了算了,扯远了,既然能找到这儿来,肯定是在vue.js上 ...

  8. 堆糖瀑布流完整解决方案(jQuery)

    2010年堆糖创办以来,网站界面经历过3-5次重大改版,logo也曾更换过两次,早期蓝红相间三个圈的logo恐怕很少有人记得了.与此同时,前端 js 框架也在默默的更新换代.最早堆糖上线时,js 采用 ...

  9. 用vue.js写的一个瀑布流的组件

    用vue.js写的一个瀑布流的组件:https://segmentfault.com/a/1190000010741319 https://www.jianshu.com/p/db3cadc03402

随机推荐

  1. Vue模板语法与常用指令

    Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据.在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数,结合相应系统,在应用状态改变时 ...

  2. .net持续集成sonarqube篇之 sonarqube集成单元测试

    系列目录 通过前面章节我们可以看到,不论怎么构建,单元测试覆盖率一栏总是0% 这是因为单元测试覆盖率报告需要额外集成.这一节我们就讲解如何在sonarqube里集成单元测试覆盖率报告. 这里需要借助O ...

  3. python模块知识一 自定义模块、time、datetime时间模块

    第五章 模块 1.自定义模块: 模块分类: 内置模块(标准库)-- python解释器自带的.py文件(模块) 第三方模块(各种大神写的)-- 需要额外下载(并发编程pypi) 自定义模块(自己写的) ...

  4. 并查集_HDU 1232_畅通工程

    某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可). ...

  5. vue教程二 vue组件(1)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <script ...

  6. python创建虚拟环境(Windows)

    >>>构建Python虚拟环境的目的是为了防止真实环境被破坏!!! >>>每一个项目建议用一个虚拟环境为了防止软件版本号冲突!!! 1.在终端切换到一个新的磁盘 如 ...

  7. Python基础总结之认识lambda函数、map函数、filter() 函数。第十二天开始(新手可相互督促)

    今天周日,白天在学习,晚上更新一些笔记,希望对大家能更好的理解.学习python~ lambda函数,也就是大家说的匿名函数.它没有具体的名称,也可以叫做一句话函数,我觉得也不过分,大家看下代码,来体 ...

  8. 03、Swagger2和Springmvc整合详细记录(爬坑记录)

    时间 内容 备注 2018年6月18日 基本使用 spirngmvc整合swagger2 开始之前这个系列博文基本是,在项目的使用中一些模块的内容记录,但是后期逐渐优化,不单单是整合内容. swagg ...

  9. 【React踩坑记三】React项目报错Can't perform a React state update on an unmounted component

    意思为:我们不能在组件销毁后设置state,防止出现内存泄漏的情况 分析出现问题的原因: 我这里在组件加载完成的钩子函数里调用了一个EventBus的异步方法,如果监听到异步方法,则会更新state中 ...

  10. .Net异步编程详解入门

    前言 今天周五,早上起床晚了.赶着挤公交上班.但是目前眼前有这么几件事情.刷牙洗脸.泡牛奶.煎蛋.在同步编程眼中.先刷牙洗脸,然后烧水泡牛奶.再煎蛋,最后喝牛奶吃蛋.毫无疑问,在时间紧促的当下.它完了 ...