我知道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. linux基础命令期末考试总结

    1.关闭防火墙:service iptables stop 2.启动防火墙:service iptables start 3.mount命令:挂载某一设备使之成为某个目录名称 4.NFS服务:linu ...

  2. 10w数组去重,排序,找最多出现次数(精华)

    package cn.tedu.javaweb.test; import java.util.*; /* * @author XueWeiWei * @date 2019/6/11 8:19 */@S ...

  3. Nginx安装(详细版本)

    Nginx安装文档 前言: 最近,系统部署人员那边,让我们给写一个傻瓜式的Nginx安装过程.所以就有了这个文档,本着独乐乐不如众乐乐,就分享一下.我觉得对入门小白来说,有图,乃至运行过程图,是很重要 ...

  4. 关于定时器Scheduled(cron)的问题

    定时器配置步骤参考:http://blog.csdn.NET/sd4000784/article/details/7745947 下面给出cron参数中各个参数的含义: CRON表达式    含义 & ...

  5. ubuntu下借助qt creator创建属于自己的共享库

    简介: 在 Windows 上,共享库由 .dll 表示:在 Linux 上,由 .so 表示. Shared Library的优势 共享库,又称动态库或so文件,顾名思义,它可以在可执行文件启动时加 ...

  6. PID算法 旋转倒立摆与平衡车的区别。此贴后边会更新。

    我做PID算法的背景和经历:本人之前电子信息科学与技术专业,对控制方向颇感兴趣,刚上大学时听到实验室老师说PID算法,那年在暑假集训准备全国电子设计竞赛,我正在练习做一个以前专科的题目,帆板角度控制系 ...

  7. Another option to bootup evidence files

    When it comes to booting up evidence files acquired from target disk, you got two options. One is VF ...

  8. javaweb基础整理随笔------jstl与el表达式

    虽然jsp中可以写java代码,但是现在不推荐这么做. jsp虽然本质是servlet,但是主要作用只是视图,视图的任务就是显示响应,而不是在JSP中做任何关于程序控制和业务逻辑的事情.所以在JSP页 ...

  9. java学习-NIO(四)Selector

    这一节我们将探索选择器(selectors).选择器提供选择执行已经就绪的任务的能力,这使得多元 I/O 成为可能.就像在第一章中描述的那样,就绪选择和多元执行使得单线程能够有效率地同时管理多个 I/ ...

  10. JS中map()与forEach()的区别和用法

    相同点: 1.都是循环遍历数组中的每一项 2.每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(索引值),arr(原数组) 3.匿名函数中的this都是指向window 4 ...