vue实现瀑布流

        本文中所采用的纯css实现瀑布流的效果并不是很好分析如下(个人见解,可能会有错误,请见谅):

        1.如果要实现下拉加载更多功能的话就不能用视频(https://www.bilibili.com/video/BV1xa4y147JP)中的第二种方法。虽然第二种方法可以实现图像的横向排序(第一行显示序号为0~n),但是需要固定距离,我觉得用这个方法的话有所限制,也可能我理解不够,见谅哈。
        2.过要实现图片的横向排序的话,就不能用视频(https://www.bilibili.com/video/BV1xa4y147JP)中的第一种方法,因为第一种方法图像时纵向排序的(第一列为0~n,第二列为n+1~2n,......)。这样对之后上传新的图片不是很友好,因为新的始终是在第一列或者最后一列,按常理来讲,新图片应该是放在第一行最好。

        最后我采用的是js实现瀑布流(这玩意确实是*折磨)。参考链接为"https://segmentfault.com/a/1190000012621936"(这个很重要)。中心思想:1.预先设定好要多少列(这里是更具我自己需求来的)2.排列第一行,动态设置每个盒子的left就可以排列好3.排列好第一行后,获取第一行盒子的高度,并且存到一个数组arr中,因为第二行的排列只需要考虑top值(这里可以适当加一个值,让上下两张图片有间隙)4.排列第二行,以此类推。详细的可以看那位作者的文章(上面的连接),里面的说明更加具体。5.在mounted中进行监听,以便随着窗口的改变,里面的item也跟着改变。 

         我用的是vue2,据说是要避免直接操作dom(我也还没遇到直接操作导致的bug),我用的是ref来操作的,相关代码贴图如下(我自己写的网站还不知道怎么实现贴代码,且不打乱格式,见谅哈)

实验结果 

vue实现瀑布流

vue实现瀑布流 

代码部分

 html代码

    <div class="picList">
      <div class="masonry" ref="pictest">
        <div class="item" v-for="(picture,index) in piclist" :key="index">
          <!-- 删掉一个ifload@load="ifLoad" -->
          <!--判断图片是否加载好-->
          <img :src="picture.pic" :alt="index" @click="showBigPic(index)" class="picture" @load="ifLoad">
          <el-dialog
            title="大图"
            :visible.sync="centerDialogVisible"
            width="50%"
            center>
            <img :src="bigPic" alt="" style="width:96%">
            <span slot="footer" class="dialog-footer">
              <el-button @click="centerDialogVisible = false">取 消</el-button>
              <el-button type="primary" @click="centerDialogVisible = false">确 定</el-button>
            </span>
          </el-dialog>
          <div class="picName">{{picture.name}}</div>
          <div class="icon">
            <!-- 点赞 -->
            <i class="iconfont like" :ref="'picLike'+index" @click="like(index)">&#xe622;</i>
            <!-- 收藏 -->
            <i class="iconfont collect" :ref="'picCollect'+index" @click="collect(index)">&#xe616;</i>
            <!-- 评论可以不用变色  <i class="iconfont comment" :ref="'picComment'+index" @click="comment(index)">&#xe615;</i> -->
            <i class="iconfont comment">&#xe615;</i>
          </div>
        </div>
      </div>
    </div>

css代码

  .picList{
    padding: 20px; // 这个会导致this.$refs.pictest.clientWidth多20
    width: 95%;
    margin: 0px auto;
    box-sizing: border-box;
    list-style: none;
    background-color: #fff;
    border-radius: 10px;
    .masonry{
      // float: left;
      position: relative;
      overflow: hidden;
      border-radius: 10px;
      margin: 0;
      background-color: rgb(233, 233, 233);
      .item{
        // float: left;
        position: absolute;
        max-width: 260px; // 设置最大的width,避免loadmore时太大影响观感
        background-color: #fff;
        border-radius: 10px;
        img{
          display: block;
          text-align: center;
          width: 95%;
          // height: auto;
          border-radius: 10px;
          margin: 10px auto;
          cursor: pointer;
        }
        .picName{
          padding-left: 10px;
          padding-bottom: 5px;
        }

js代码

        data部分

  data() {
    return {
      scrollHeight: 0, // 滚动的高度
      itemWidth: 0 + 'px', // 每个item 的宽度
      showPicList: [], // 获取全部图片的src
      piclist: [], // 获取showPicList中部分图片进行显示,之后再从loadmore()中获取更多(下拉加载功能)
      bigPic: '',
      centerDialogVisible: false,
      loadCount: 0
      // width: this.$refs.pictest.clientWidth
    }
  },

        methods部分 

    waterfall() {
      var items = this.$refs.pictest.children
      // var itemWidth = 270
      var columns1 = 4
      var arr = []
      // 距离左右边缘的距离固定,item的缝隙固定
      var gapToTop = 10 // 与顶部之间的距离,避免盒子重合
      var gap = 20 // 上下间距
      var gapBetween = 10 // 左右item之间的距离
      var gapToSide = 10 // 到masonry左、右边缘的距离
      // var calcItemWidth = (this.$refs.pictest.clientWidth - 2 * gapToSide - (columns1 - 1) * gapBetween) / 4
      console.log(this.$refs.pictest.clientWidth)
      // 实现左右对称
      // items[0].style.left = gapToSide + 'px'
      // items[columns1 - 1].style.right = gapToSide + 'px'
      var calcItemWidth = (this.$refs.pictest.clientWidth - 2 * gapToSide - (columns1 - 1) * gapBetween) / 4
      this.itemWidth = calcItemWidth + 'px'
      for (var i = 0; i < items.length; i++) {
        this.$refs.pictest.children[i].style.width = calcItemWidth + 'px'
        console.log(calcItemWidth)
        // console.log(items[i].offsetHeight, items[i].clientHeight, items[i].offsetWidth, items[i].clientWidth)
        if (i < columns1) {
          items[i].style.top = gapToTop + 'px'
          // 调节第一行偏移量,让整个瀑布流区域居中
          // items[i].style.left = (calcItemWidth + gapBetween) * i + gapToSide + 'px'
          items[i].style.left = (calcItemWidth + gapBetween) * i + gapToSide + 'px'
          // console.log(items[i].style.top)
          arr.push(items[i].offsetHeight + gap)
          // console.log(items[i].offsetHeight)
        } else {
          var minHeight = arr[0]
          var index = 0
          for (var j = 0; j < arr.length; j++) {
            if (minHeight > arr[j]) {
              minHeight = arr[j]
              index = j
            }
          }
          items[i].style.top = arr[index] + gapToTop + 'px'
          // console.log(arr[0])
          items[i].style.left = items[index].offsetLeft + 'px'
          arr[index] = arr[index] + items[i].offsetHeight + gap
        }
      }
      // console.log(Math.max(...arr))
      this.$refs.pictest.style.height = Math.max(...arr) + 25 + 'px'
    },
    // 等图片加载完后执行瀑布流函数
    ifLoad() {
      this.loadCount++
      if (this.loadCount === this.$refs.pictest.children.length) {
        console.log(this.loadCount)
        this.waterfall()
      }
    },
    // 下拉加载更多
    loadMore() {
      var piclistlen = this.piclist.length
      var showPicListlen = this.showPicList.length
      if (showPicListlen - piclistlen >= 4) {
        this.piclist.push(...(this.showPicList.slice(piclistlen, piclistlen + 4)))
        // this.$refs.pictest.children.map(child => { child.style.width = that.itemWidth })
      } else {
        this.piclist.push(...(this.showPicList.slice(piclistlen, showPicListlen)))
      }
    },

         mounted部分

  mounted() {
    // console.log(document.getElementsByClassName('masonry'))
    window.addEventListener('resize', () => {
      this.waterfall()
    })
    window.addEventListener('scroll', () => {
      console.log(window.scrollY)
      var that = this
      if (window.scrollY > that.scrollHeight) {
        if (window.scrollY - that.scrollHeight >= 100) {
          this.loadMore()
          this.scrollHeight = window.scrollY
        }
      }
    })
  },

补充说明

  没有图片素材的同学可以尝试一下下面提到的这个网址,加到img的src就可以了(https://picsum.photos/360/400)  第一个参数360是指图片宽度,第二个参数400是指图片高度,每次都是会随机显示不同图片的,可以用代码把这些src放到一个arr里面,然后v-for循环显示。例子如下:

    for (let i = 0; i <= 19; i++) {
      const height = Math.floor((Math.random() * 200) + 201)
      this.piclist.push('https://picsum.photos/360/' + height + '?random=' + (i + 1))
    }

参考连接

https://www.bilibili.com/video/BV1xa4y147JP

https://segmentfault.com/a/1190000012621936(多看这个,这个很重要)

上一篇:CSS(Cascading Style Sheet)经验总结


下一篇:2021/11/15