瀑布流式页面布局怎么写

害嗨嗨,我又来了奥。今天呢,我们讨论的话题是瀑布流式布局。我们打开淘宝,往下滑,就可以看到很多个商品从上往下排列。但是每个商品的div所占的高度有不一样,这种布局叫瀑布流式布局。页面上是一种 参差不齐 的多栏式的布局,类似上图所示随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部,图片固定宽度,高度却不一样,根据原比例缩放到宽度达到固定的要求,每行排满后,新的图片添加到总高度最小的那一列

一句话概括,就是先排一行(两列),从第二行开始,每一个图片加到目前已经排过的高度最小的图片下面。每当我们放一张图,那一列的高度就会发生相应的变化。下面,我们用vue2框架,来讲讲该如何简单地写一个瀑布流式布局。

先上template代码:

<template>
  <div class="box">
    <!-- for循环waterfallList中的每一项,包括背景色background、左距离left、上距离top、高度height,每一项都是绝对定位,根据left和top控制位置,所以父元素box要设置成相对定位 -->
    <div
      class="items"
      v-for="(item, i) in waterfallList"
      :key="i"
      :style="{
        background: item.background,
        left: item.left + 'px',
        top: item.top + 'px',
        height: item.height + 'px',
      }"
    ></div>
  </div>
</template>

其中,waterfallList是一个包括背景色background、左距离left、上距离top、高度height的对象组成的数组,这样的话,每一个循环的div就要设置为绝对定位,则它的父元素box得设置成相对定位,以下是scss代码

<style lang="scss" scoped>
.box {
  //父元素相对定位
  position: relative;
  border: 1px solid #ccc;
  .items {
    //子元素绝对定位
    width: 200px;
    position: absolute;
  }
}
</style>

现在,得写js部分了。我们首先在data里存几个变量。其中list是原数据(即实际项目中后端返回的,这里省略大多数部分,只设置了div的高度和颜色,图片的url等参数全部省略)。waterfallList前面已经说过了。heightList是一个保存了所有height的数组。 数组里全是数字,为了后面比较大小 。学过ts的同学应该知道可以表示为heightList: number[]

<script>
export default {
  data() {
    return {
      //所有要显示的数据最开始的列表,后面要经过计算形成waterfallList数组再显示在页面上
      list: [
        {
          height: 300,
          background: "red",
        },
        {
          height: 310,
          background: "pink",
        },
        {
          height: 320,
          background: "blue",
        },
        {
          height: 290,
          background: "green",
        },
        {
          height: 300,
          background: "gray",
        },
        {
          height: 310,
          background: "#CC00FF",
        },
        {
          height: 290,
          background: "black",
        },
        {
          height: 280,
          background: "#996666",
        },
        {
          height: 320,
          background: "skyblue",
        },
        {
          height: 300,
          background: "#993366",
        },
        {
          height: 280,
          background: "#33FF33",
        },
        {
          height: 310,
          background: "skyblue",
        },
        {
          height: 290,
          background: "#6633CC",
        },
        {
          height: 300,
          background: "#666699",
        },
        {
          height: 300,
          background: "#66CCFF",
        },
        {
          height: 300,
          background: "skyblue",
        },
        {
          height: 290,
          background: "#CC3366",
        },
        {
          height: 290,
          background: "#CC9966",
        },
        {
          height: 290,
          background: "#FF00FF",
        },
        {
          height: 320,
          background: "#990000",
        },
        {
          height: 310,
          background: "red",
        },
        {
          height: 280,
          background: "#999966",
        },
        {
          height: 290,
          background: "#CCCC66",
        },
        {
          height: 300,
          background: "#FF33FF",
        },
        {
          height: 310,
          background: "#FFFF66",
        },
        {
          height: 290,
          background: "red",
        },
        {
          height: 280,
          background: "skyblue",
        },
        {
          height: 290,
          background: "#33CC00",
        },
        {
          height: 300,
          background: "#330033",
        },
        {
          height: 280,
          background: "#0066CC",
        },
        {
          height: 290,
          background: "skyblue",
        },
        {
          height: 280,
          background: "#006666",
        },
        {
          height: 290,
          background: "yellow",
        },
        {
          height: 300,
          background: "yellow",
        },
        {
          height: 280,
          background: "#33CCFF",
        },
        {
          height: 310,
          background: "yellow",
        },
        {
          height: 310,
          background: "yellow",
        },
        {
          height: 290,
          background: "#33FF00",
        },
        {
          height: 300,
          background: "yellow",
        },
        {
          height: 280,
          background: "green",
        },
      ],
      waterfallList: [], //保存所有list的top和left信息
      heightList: [], //保存所有height 数组里全是数字,为了后面比较大小 即heightList:number[]
    };
  },
};
</script>

下面是最重要的methods部分了,请看

<script>
export default {
  methods: {
    initWaterfall() {
      //比css设置的宽度200px大一点,这样就不会粘在一起,一行两个,两者之间距离为10
      let width = 210;
      //每次初始化的时候要把数据清空,防止直接在很多数据上继续加,导致数量超出
      this.waterfallList = []; //存着一个又一个的对象
      this.heightList = []; //存着所有列目前的总高度,用来判断下一个加在哪个位置
      //只放两列
      for (let i = 0; i < this.list.length; i++) {
        if (i < 2) {
          //第一行,直接摆
          //第一行的top距离都是0
          this.list[i].top = 0;
          //第一行的left左边距离根据列数乘以固定宽度求得
          this.list[i].left = i * width;
          //放进瀑布列表,作为初始化项,template中的代码先显示一行两列
          this.waterfallList.push(this.list[i]);
          //每一行的高度初始化
          this.heightList.push(this.list[i].height);
        } else {
          //找出最小的高度
          let current = Math.min(...this.heightList);
          //找出最小高度对应的index,最后一个最小的
          let index = this.heightList.lastIndexOf(current);
          //top为最小高度+20  为了跟上方有20的距离
          this.list[i].top = current + 20;
          //left就为最小高度的序号乘以宽度
          this.list[i].left = index * width;
          //改变这个最小的高度了,要加上前面多加的20以及新增的元素自己的高度
          this.heightList[index] += 20 + this.list[i].height;
          //在要显示的列表中把这个list的项加进去
          this.waterfallList.push(this.list[i]);
        }
      }
    },
  },
  mounted() {
    //页面加载的时候,就初始化这个瀑布流,然后就可以直接看到了
    this.initWaterfall();
  },
};
</script>

一行一行的讲解:

首先,css中设置了每个div宽为200px,所以这里设置一个变量width为210,多出的10像素是为了保证每列之间有个间距。每次初始化的时候,要讲waterfallList和heightList清空(可能会遇到不是两列的情况,根据浏览器视口宽度clientWidth或者盒子的宽度来计算最多可以放多少列,然后页面每次resize的时候调用这个方法,所以要清空一下。)大家可以思考一下这个要怎么做,本文只说两列的情况。

遍历这个list,如果index小于2,即现在还是在第一个行设置布局,则直接拜访,左边的距离为i*变量width,上面的距离都为0。然后计算好放进waterfallList数组中,再将现在每一列的高度放在heightList数组中。

从第二列开始,就要做判断了,判断heightList数组中哪个最小,虽然这里只有两个元素,但是这样子是通用的做法,直接用三个点结合Math.min最小的,然后找到它的index(如果有多个一样的,默认放在最后一个最小的下面)。随后,该项的left就是这个最小高度的index乘以宽度,top就是最小的高度+20(上下保持20间距),然后放进waterfallList数组中再把heightList的那一项加上新增的高度以及间距20。这样就可以解决问题。我们看图

优点如下
节省空间:降低页面的复杂
对于触屏设备非常友好:通过向上滑动浏览,交互方式更符合直觉
良好的视觉体验:浏览时不会被页面整整齐齐的高度影响,参差不齐,降低浏览的疲劳

万物皆有两面性
缺点如下
内容总长度 无法掌握
数据过多时,容易造成页面加载的负荷
再次加载时 很难定位上一次浏览的内容

 好了,这次就分享到这,大家去试一试吧。顺便把我上方出的思考题想一想,下次再见!

猜你喜欢

转载自blog.csdn.net/weixin_68067009/article/details/128208809