laizimo / zimo-article

:books:博客——源于实践,乐于分享,欢迎Star~
1.06k stars 95 forks source link

瀑布流列表的下拉刷新和上拉加载实现(上)【缺图】 #17

Open laizimo opened 6 years ago

laizimo commented 6 years ago

前言

前段时间,在投票系统中实现了h5的瀑布流加载,demo地址。整体的demo实现效果上可分成三个部分:瀑布流+下拉刷新+上拉加载。下面就具体来分析一下如何实现的。

正文

首先,瀑布流的实现方式只要有三种:

  1. 在获取内容时,计算出内容的高度,然后使用定位的方式来形成瀑布流(这种方式目前比较常用,但是需要实时计算,性能不是特别高)。

  2. 获得页面的宽度,然后根据页面的宽度计算列数,在进行固定列表的插入(我是用的就是这一种)

  3. 使用css3的列布局,由于兼容性问题需要使用前缀(-moz,-webkit)

由于本人使用的是第二种方式,因此,我就详细分析一下第二种方式。

由于设计图是640px的,且整体分成两列,所以,对于列数这个概念,我们可以设定为2,当然了,如果你是PC端的,并不确定列数,你也可以通过window.innerWidth来获取整体的宽度,然后相对于每列的宽度做一个除法。

分析一下整体的html内容部分,主要分为外层的ul,对应的li,以及li里面的内容。

<ul class="container">
        <li class="list">
      <div class="list-item item1">

      </div>
      <div class="list-item">

      </div>
    </li>
    <li class="list">
      <div class="list-item">

      </div>
      <div class="list-item">

      </div>
    </li>
</ul>

然后设置css的样式时,将ul的布局设置为flex布局。

.container{
  display: flex;
  list-style: none;
  padding: 0;
}

.list{
  flex: 1;
  margin: 10px; 
}

.list-item{
  width: 100%;
  height: 200px;
  margin-top: 20px;
  background: red;
}

.item1{
  height: 100px;
}

这样大致的效果图如下:

waterfall

之后,我们来写div块的模板函数,方便之后获取数据的插入tepl()

templ: function(data){
            const block = doc.createElement('div');
            block.className = 'list-item';
            const value = `
                <img src="${data.image}" alt="person-image">
                <div class="name">${data.name}</div>
                <div class="info">
                    <span class="id">编号: ${data.id}</span>
                    <span class="vote"><span class="count">${data.vote}</span>票</span>
                </div>
                <div class="desc">
                        ${data.desc}
                </div>
                <div class="btn-group">
                    <button class="btn detail" data-value="${data.id}">候选详情</button>
                    <button class="btn vote" data-value="${data.id}">为TA投票</button>
                </div>`;
            block.innerHTML = value;
            return block;
        }

该函数中,就是创建了一个div块,然后给div块赋上classname,之后就是一些固定数据的插入(如果会使用js模板引擎的话,也可以使用,推荐ejs吧)。

之后写一个函数将获取到的数据插入到列表中(由于,我这里还考虑了列表不确定的情况,所以li也是需要临时生成的,同时还需要一个list_number去记录列表数)。先来看一下这部分的函数:

setHtml: function(data){
            const _self = this;
            const fragments = _self.setFragements(data);
            $('.list-wrapper').each((index, item) => {
                $(item).append(fragments[index]);
            });
        },
        initHtml: function(data){     //initial html template
            const _self = this;
            const fragment = doc.createDocumentFragment();
            const fragments = _self.setFragements(data);
            fragments.forEach((item, index) => {
                const listItem = doc.createElement('li');
                listItem.className = 'list-wrapper';
                listItem.appendChild(item);
                fragment.appendChild(listItem);
            });
            $('.wrapper').html(fragment);
        },
        setFragements: function(data){      //generate some fragment
            const _self = this;
            const count = _self.config['list_number'];
            const fragments = _self.getFragments(count);
            data.forEach((item, index) => {
                const i = index % count;
                const block = _self.templ(item);
                fragments[i].appendChild(block);
            });
            return fragments;
        },
        getFragments: function(num){
            const arr = [];
            for(let i = 0; i < num; i++){
                arr.push(doc.createDocumentFragment());
            }
            return arr;
        }

这里的函数主要分为几个功能:插入到html中(非第一次)、初始化html部分、创建fragment。

主要的思路就是第一次加载的时候创建对应的li数量,然后通过fragment片段的形式插入ul中,之后的加载只要获取li的内容,在它的后面进行添加就可以了。

总结

这部分主要是瀑布流以及模板的加载,之后的一篇将会讲下拉刷新和上拉加载的主要思路与实现。整体demo的源码地址在我的github上(demo地址