瀑布流布局

最近浏览到一个网站,[FILMGRAB],然后仿照这个网站写了个瀑布流的demo,需要类似功能的可以参考一下。

瀑布流又称瀑布流式布局,是比较流行的一种网页布局方式,每张图片的宽度都设置为一样的,但是高度是根据内容变化的,实现一个不规则的排列。

当然实现瀑布流布局的方式有很多,有不好的地方,欢迎大家来评论区指导。

实现思路

1、根据视图区域的宽度判断需要几列

2、将图片插入到每一列并记录每一列图片的位置(top),成为每一列的首个元素

3、此时每一列都有数据,找到高度最小的一列,记录当前索引,然后插入数据,重复此操作直至所有数据都插入进去。

直接上代码

首先准备一些图片,我直接找到一些图片链接,简单处理下格式

const imgArr = [
      "https://film-grab.com/wp-content/uploads/2022/07/The-Adjuster-006.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Live-and-Let-Die-061.jpg",
      "https://film-grab.com/wp-content/uploads/2022/07/After-Yang026.jpg",
      "https://film-grab.com/wp-content/uploads/2022/07/The-Worst-Person-in-the-World-001.jpg",
      "https://film-grab.com/wp-content/uploads/2020/10/Zeroville-025.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Bigger-Than-Life-029.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Diamonds-are-Forever-060.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Prisoners-of-Ghostland-002.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Dune-2021-022.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Celeste-005.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/On-Her-Majestys-Secret-Service-023.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/NightmareAlley010.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/Paris-13th010.jpg",
      "https://film-grab.com/wp-content/uploads/2020/10/Taking-Off-019.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/The-Eyes-of-Tammy-Faye-001.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/CmonCmon019.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Frighteners-021.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/You-Only-Live-Twice-003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/In-Which-We-Serve-008.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/Apollo101_2036.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/InTheEarth035.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/3Iron042.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/Femme-Fatale049.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Thunderball-005.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/The-Card-Counter-032.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/The-Apartment003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Goldfinger-015.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/His-House-047.jpg",
      "https://film-grab.com/wp-content/uploads/2021/09/Der-Prozess-004.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Dillinger-is-Dead-028.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Domain-007.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/From-Russia-With-Love-002.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Master-Z-052.jpg",
      "https://film-grab.com/wp-content/uploads/2019/10/Friday-the-13th-Part-5-056.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Universal-Soldier-The-Return-003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/Intrusion016.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/The-Canterbury-Tales-016.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Universal-Soldier-008.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Ip-Man-4-047.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/Shadow-Film-01.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/Les-Chants-de-Maldoror-011.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Desert-Hearts-024.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Straight-Time-005.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Ip-Man-3-006.jpg",
      "https://film-grab.com/wp-content/uploads/2021/09/Emperor-Tomato-Ketchup-020.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Black-Widow-009.jpg",
      "https://film-grab.com/wp-content/uploads/2020/10/Border-006.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Green-Inferno-029.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/The-Damned-013.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Ip-Man-2-054.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Passing-001.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Master-of-the-House-003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Outside-Satan-003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Black-Orpheus-043.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Ip-Man-001.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Last-Night-In-Soho-023.jpg",
      "https://film-grab.com/wp-content/uploads/2021/09/Explorers-039.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/Me-and-Earl-028.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Annette-014.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/The-Rubber-05.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/Hana-Bi-046.jpg"
    ].map((el) => {
      return {
        src: el,
        name: el.substring(49)
      };
    });

然后预加载把图片的高度保存下来

picLoad() {
  return new Promise((resolve) => {
    this.imgArr.forEach((item) => {
      if (!item.src) {
        this.loadedCount++;
        item["_height"] = 0;
        if (this.loadedCount == this.imgArr.length) {
          resolve();
        }
      } else {
        let img = new Image();
        img.src = item.src;
        img.onload = () => {
          this.loadedCount++;
          // 预加载图片的高 / 预加载图片的宽 === 实际渲染图片的高 / 实际渲染图片的宽。
          // 我们知道实际渲染图片的宽,所以可以用 -- 实际宽 * (预加载高 / 预加载宽) -- 这个公式得出实际渲染图片的高。
          item["_height"] = Math.round(this.imgItemWidth * (img.height / img.width));
          if (this.loadedCount == this.imgArr.length) {
            resolve();
          }
        };
        img.onerror = () => {
          this.loadedCount++;
          item["_height"] = 0;
          if (this.loadedCount == this.imgArr.length) {
            resolve();
          }
        };
      }
    });
  });
},

以上操作实际项目中可以交给后台处理,不管什么方式,最后拿到图片资源的高度就行。

根据视图区域的宽度判断需要几列。

let clientWidth = this.$refs.container.clientWidth, // 页面视图容器宽度
    // 列数 = 容器宽度 / (图片宽度 + 间隙)
  cols = Math.floor(clientWidth / (this.imgItemWidth + this.gap)); // 显示列数

将图片插入到每一列并记录每一列图片的位置(top)

  let columns = []; // 存放每一列的数据
  // 初始每列首行元素
  for (let i = 0; i < cols; i++) {
    this.imgArr[i]["_top"] = 0;
    columns.push({ picList: [this.imgArr[i]] });
  }

 此时每一列都有数据,找到高度最小的一列,记录当前索引,然后插入数据,重复此操作直至所有数据都插入进去。

// 处理剩余元素
  for (let i = cols; i < this.imgArr[i].length; i++) {
    // 找到每列最小高度和最小高度的索引
    let minHeight = this.getMin(columns);
    // 最小高度就是下个图片的top
    this.imgArr[i]["_top"] = minHeight.height;
    // 插入数据
    columns[minHeight.index]["picList"].push(this.imgArr[i]);
  }

getMin方法

getMin(columns) {
  let index = 0,
    min = columns.reduce((prev, cur, i) => {
      let _cur = cur.picList[cur.picList.length - 1],
        h = parseInt(_cur._top) + parseInt(_cur._height);
      if (prev == 0) {
        return h;
      } else if (h < prev) {
        index = i;
        return h;
      } else {
        return prev;
      }
    }, 0);
  return { height: min, index };
}

瀑布流使用flex布局,然后加上一些css过渡属性

<div class="container" ref="container">
  <div class="col" v-for="(col, index) in columns" :key="index">
    <div
      ref="pic"
      class="pic"
      v-for="item in col.picList"
      :key="item.name"
      :style="{ width: `${imgItemWidth}px`, height: `${item._height}px` }"
    >
      <img :src="item.src" style="width: 100%" />
      <div class="pic-mask" :style="{ height: `${item._height}px` }">
        <div class="pic-name">{{ item.name }}</div>
      </div>
    </div>
  </div>
</div>
<style lang="scss" scoped>
.container {
  width: 100%;
  display: flex;
  justify-content: space-around;
  flex-wrap: wrap;
  padding: 0 30px 20px;
  .pic {
    overflow: hidden;
    border-radius: 10px;
    margin-top: 20px;
    transition: all 0.6s;
    &-mask {
      width: 100%;
      background-color: rgba(0, 0, 0, 0);
      transition: background-color 4s;
      transform: translateY(-100%);
      display: flex;
      align-items: flex-end;
      justify-content: center;
      .pic-name {
        height: 30px;
        text-align: center;
        font-weight: bold;
        color: transparent;
        transform: translateY(30px);
        transition: all 1s;
      }
    }
    &-mask:hover {
      background-color: rgba(0, 0, 0, 0.4);
      transition: background-color 4s linear 0.5s;
      .pic-name {
        color: #fff;
        transform: translateY(0px);
        transition: all 1s;
      }
    }
  }
  .pic:hover {
    transform: translateY(-10px);
    transition: all 0.6s;
  }
}
</style>

就完成了。

完整代码

<template>
  <div class="container" ref="container">
    <div class="col" v-for="(col, index) in columns" :key="index">
      <div
        ref="pic"
        class="pic"
        v-for="item in col.picList"
        :key="item.name"
        :style="{ width: `${imgItemWidth}px`, height: `${item._height}px` }"
      >
        <img :src="item.src" style="width: 100%" />
        <div class="pic-mask" :style="{ height: `${item._height}px` }">
          <div class="pic-name">{{ item.name }}</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "waterfall",
  data() {
    const imgArr = [
      "https://film-grab.com/wp-content/uploads/2022/07/The-Adjuster-006.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Live-and-Let-Die-061.jpg",
      "https://film-grab.com/wp-content/uploads/2022/07/After-Yang026.jpg",
      "https://film-grab.com/wp-content/uploads/2022/07/The-Worst-Person-in-the-World-001.jpg",
      "https://film-grab.com/wp-content/uploads/2020/10/Zeroville-025.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Bigger-Than-Life-029.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Diamonds-are-Forever-060.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Prisoners-of-Ghostland-002.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Dune-2021-022.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Celeste-005.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/On-Her-Majestys-Secret-Service-023.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/NightmareAlley010.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/Paris-13th010.jpg",
      "https://film-grab.com/wp-content/uploads/2020/10/Taking-Off-019.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/The-Eyes-of-Tammy-Faye-001.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/CmonCmon019.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Frighteners-021.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/You-Only-Live-Twice-003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/In-Which-We-Serve-008.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/Apollo101_2036.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/InTheEarth035.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/3Iron042.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/Femme-Fatale049.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Thunderball-005.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/The-Card-Counter-032.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/The-Apartment003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Goldfinger-015.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/His-House-047.jpg",
      "https://film-grab.com/wp-content/uploads/2021/09/Der-Prozess-004.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Dillinger-is-Dead-028.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Domain-007.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/From-Russia-With-Love-002.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Master-Z-052.jpg",
      "https://film-grab.com/wp-content/uploads/2019/10/Friday-the-13th-Part-5-056.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Universal-Soldier-The-Return-003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/05/Intrusion016.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/The-Canterbury-Tales-016.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Universal-Soldier-008.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Ip-Man-4-047.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/Shadow-Film-01.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/Les-Chants-de-Maldoror-011.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Desert-Hearts-024.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Straight-Time-005.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Ip-Man-3-006.jpg",
      "https://film-grab.com/wp-content/uploads/2021/09/Emperor-Tomato-Ketchup-020.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Black-Widow-009.jpg",
      "https://film-grab.com/wp-content/uploads/2020/10/Border-006.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/Green-Inferno-029.jpg",
      "https://film-grab.com/wp-content/uploads/2022/02/The-Damned-013.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Ip-Man-2-054.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Passing-001.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Master-of-the-House-003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Outside-Satan-003.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Black-Orpheus-043.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Ip-Man-001.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Last-Night-In-Soho-023.jpg",
      "https://film-grab.com/wp-content/uploads/2021/09/Explorers-039.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/Me-and-Earl-028.jpg",
      "https://film-grab.com/wp-content/uploads/2022/04/Annette-014.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/The-Rubber-05.jpg",
      "https://film-grab.com/wp-content/uploads/2021/10/Hana-Bi-046.jpg"
    ].map((el) => {
      return {
        src: el,
        name: el.substring(49)
      };
    });
    return {
      imgArr,
      columns: [],
      loadedCount: 0,
      imgItemWidth: 360, // 图片宽度
      gap: 20, // 间隙
      times: null
    };
  },
  async mounted() {
    await this.picLoad();
    this.initData();
    window.onresize = () => {
      if (this.times) {
        clearTimeout(this.times);
      }
      this.times = setTimeout(() => {
        this.initData();
      }, 500);
    };
  },
  methods: {
    picLoad() {
      return new Promise((resolve) => {
        this.imgArr.forEach((item) => {
          if (!item.src) {
            this.loadedCount++;
            item["_height"] = 0;
            if (this.loadedCount == this.imgArr.length) {
              resolve();
            }
          } else {
            let img = new Image();
            img.src = item.src;
            img.onload = () => {
              this.loadedCount++;
              item["_height"] = Math.round(this.imgItemWidth * (img.height / img.width));
              if (this.loadedCount == this.imgArr.length) {
                resolve();
              }
            };
            img.onerror = () => {
              this.loadedCount++;
              item["_height"] = 0;
              if (this.loadedCount == this.imgArr.length) {
                resolve();
              }
            };
          }
        });
      });
    },
    initData() {
      let clientWidth = this.$refs.container.clientWidth, // 页面视图容器宽度
        cols = Math.floor(clientWidth / (this.imgItemWidth + this.gap)), // 计算显示列数
        columns = [];
      // 初始每列首行元素
      for (let i = 0; i < cols; i++) {
        this.imgArr[i]["_top"] = 0;
        columns.push({ picList: [this.imgArr[i]] });
      }
      // 处理剩余元素
      for (let i = cols; i < this.imgArr.length; i++) {
        let minHeight = this.getMin(columns);
        this.imgArr[i]["_top"] = minHeight.height;
        columns[minHeight.index]["picList"].push(this.imgArr[i]);
      }
      this.columns = columns;
    },
    getMin(columns) {
      let index = 0,
        min = columns.reduce((prev, cur, i) => {
          let _cur = cur.picList[cur.picList.length - 1],
            h = parseInt(_cur._top) + parseInt(_cur._height);
          if (prev == 0) {
            return h;
          } else if (h < prev) {
            index = i;
            return h;
          } else {
            return prev;
          }
        }, 0);
      return { height: min, index };
    }
  }
};
</script>

<style lang="scss" scoped>
.container {
  width: 100%;
  display: flex;
  justify-content: space-around;
  flex-wrap: wrap;
  padding: 0 30px 20px;
  .pic {
    overflow: hidden;
    border-radius: 10px;
    margin-top: 20px;
    transition: all 0.6s;
    &-mask {
      width: 100%;
      background-color: rgba(0, 0, 0, 0);
      transition: background-color 4s;
      transform: translateY(-100%);
      display: flex;
      align-items: flex-end;
      justify-content: center;
      .pic-name {
        height: 30px;
        text-align: center;
        font-weight: bold;
        color: transparent;
        transform: translateY(30px);
        transition: all 1s;
      }
    }
    &-mask:hover {
      background-color: rgba(0, 0, 0, 0.4);
      transition: background-color 4s linear 0.5s;
      .pic-name {
        color: #fff;
        transform: translateY(0px);
        transition: all 1s;
      }
    }
  }
  .pic:hover {
    transform: translateY(-10px);
    transition: all 0.6s;
  }
}
</style>

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值