溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么使用Vue實(shí)現(xiàn)一個(gè)圖片水平瀑布流插件

發(fā)布時(shí)間:2022-10-12 10:39:08 來(lái)源:億速云 閱讀:154 作者:iii 欄目:編程語(yǔ)言

本文小編為大家詳細(xì)介紹“怎么使用Vue實(shí)現(xiàn)一個(gè)圖片水平瀑布流插件”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“怎么使用Vue實(shí)現(xiàn)一個(gè)圖片水平瀑布流插件”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。

一.需求來(lái)源

項(xiàng)目主體用的是之前在學(xué)習(xí)的CRMEB的后端框架來(lái)開(kāi)發(fā),UI使用iView-UI,其余的場(chǎng)景與其他的vue項(xiàng)目一致。

二.邏輯設(shè)想

如果不是vue環(huán)境,我們的邏輯為

1.獲取所有的p元素
2.獲取盒子的寬度,寬度都是相同,高度不同
3.在浮動(dòng)布局中每一行的盒子個(gè)數(shù)不固定,是根據(jù)屏幕寬度和盒子寬度決定
4.獲取屏幕寬度
5.求出列數(shù),屏幕寬度 / 盒子寬度 取整
6.瀑布流最關(guān)鍵的是第二行的盒子的排布方式,通過(guò)獲取第一行盒子中最矮的一個(gè)的下標(biāo),絕對(duì)定位,top是最矮盒子的高度,left是最矮盒子的下標(biāo) * 盒子的寬度
7.循環(huán)遍歷所有的盒子,通過(guò)列數(shù)找到第一行所有的盒子,將第一行盒子的高度放入數(shù)組,再取出數(shù)組中最小的一個(gè)的下標(biāo),就是第6步思路的第一行盒子中最矮盒子的下標(biāo)。
8.循環(huán)繼續(xù),第二行第一個(gè)盒子,通過(guò)絕對(duì)定位,放進(jìn)頁(yè)面。
9.關(guān)鍵,需要將數(shù)組中最小的值加上放進(jìn)的盒子的高度
10.繼續(xù)循環(huán),遍歷所有
11.如果想要加載更多,需要判斷最后一個(gè)盒子的高度和頁(yè)面滾動(dòng)的距離,再將數(shù)據(jù)通過(guò)創(chuàng)建元素,追加進(jìn)頁(yè)面,再通過(guò)瀑布流布局展示

但如果是Vue項(xiàng)目,我們可以把邏輯歸結(jié)為以下幾步

1.獲取屏幕寬度
2..獲取盒子的寬度,寬度都是相同,高度不同
3.在浮動(dòng)布局中每一行的盒子個(gè)數(shù)不固定,是根據(jù)屏幕寬度和盒子寬度決定
4.求出列數(shù),屏幕寬度 / 盒子寬度 取整
5.瀑布流最關(guān)鍵的是第二行的盒子的排布方式,通過(guò)獲取第一行盒子中最矮的一個(gè)的下標(biāo),絕對(duì)定位,top是最矮盒子的高度,left是最矮盒子的下標(biāo) * 盒子的寬度
6.繼續(xù)循環(huán),遍歷所有
7.如果想要加載更多,需要判斷最后一個(gè)盒子的高度和頁(yè)面滾動(dòng)的距離,再將數(shù)據(jù)通過(guò)創(chuàng)建元素,追加進(jìn)頁(yè)面,再通過(guò)瀑布流布局展示

三.最終效果圖片

怎么使用Vue實(shí)現(xiàn)一個(gè)圖片水平瀑布流插件

四.代碼分析

先看下我的html部分

<template>
  <div class="tab-container" id="tabContainer">
    <div class="tab-item" v-for="(item, index) in pbList" :key="index">
      <img :src="item.url" />
    </div>
  </div>
</template>
 
<style scoped>
* {
  margin: 0;
  padding: 0;
}
/* 最外層大盒子 */
.tab-container {
  padding-top: 20px;
  position: relative;
}
/* 每個(gè)小盒子 */
.tab-container .tab-item {
  position: absolute;
  height: auto;
  border: 1px solid #ccc;
  box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
  background: white;
  /* 元素不能中斷顯示 */
  break-inside: avoid;
  text-align: center;
}
.tab-container .tab-item img {
  width: 100%;
  height: auto;
  display: block;
}
</style>

核心js部分

<script>
export default {
  name:'compList',
  props:{
    pbList:{
      type:Array,
      default:()=>{return []}
    }
  },
  data() {
    return {
    };
  },
  mounted() {
    this.$nextTick(()=>{
      this.waterFall("#tabContainer", ".tab-item"); //實(shí)現(xiàn)瀑布流
    })
  },
  methods: {
    waterFall(
        wrapIdName,
        contentIdName,
        columns = 5,
        columnGap = 20,
        rowGap = 20
    ) {
      // 獲得內(nèi)容可用寬度(去除滾動(dòng)條寬度)
      const wrapContentWidth =
          document.querySelector(wrapIdName).offsetWidth;
 
      // 間隔空白區(qū)域
      const whiteArea = (columns - 1) * columnGap;
 
      // 得到每列寬度(也即每項(xiàng)內(nèi)容寬度)
      const contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
 
      // 得到內(nèi)容項(xiàng)集合
      const contentList = document.querySelectorAll(contentIdName);
 
      // 成行內(nèi)容項(xiàng)高度集合
      const lineConentHeightList = [];
 
      for (let i = 0; i < contentList.length; i++) {
        // 動(dòng)態(tài)設(shè)置內(nèi)容項(xiàng)寬度
        contentList[i].style.width = contentWidth + "px";
 
        // 獲取內(nèi)容項(xiàng)高度
        const height = contentList[i].clientHeight;
 
        if (i < columns) {
          // 第一行按序布局
          contentList[i].style.top = "0px";
          contentList[i].style.left = contentWidth * i + columnGap * i + "px";
 
          // 將行高push到數(shù)組
          lineConentHeightList.push(height);
        } else {
          // 其他行
          // 獲取數(shù)組最小的高度 和 對(duì)應(yīng)索引
          let minHeight = Math.min(...lineConentHeightList);
          let index = lineConentHeightList.findIndex(
              (listH) => listH === minHeight
          );
 
          contentList[i].style.top = minHeight + rowGap +"px";
          contentList[i].style.left = (contentWidth + columnGap) * index + "px";
 
          // 修改最小列的高度 最小列的高度 = 當(dāng)前自己的高度 + 拼接過(guò)來(lái)的高度 + 行間距
          lineConentHeightList[index] += height + rowGap;
        }
      }
    },
  },
};
</script>

這里要給大家提個(gè)醒,在當(dāng)插件使用的時(shí)候,我們需要用this.$nextTick()來(lái)進(jìn)行頁(yè)面初始化,因?yàn)榉椒ǔ晒Φ那疤崾且软?yè)面初始化加載完畢后才能進(jìn)行獲取和計(jì)算

總體插件代碼為:

<template>
  <div class="tab-container" id="tabContainer">
    <div class="tab-item" v-for="(item, index) in pbList" :key="index">
      <img :src="item.url" />
    </div>
  </div>
</template>
 
<script>
export default {
  name:'compList',
  props:{
    pbList:{
      type:Array,
      default:()=>{return []}
    }
  },
  data() {
    return {
    };
  },
  mounted() {
    this.$nextTick(()=>{
      this.waterFall("#tabContainer", ".tab-item"); //實(shí)現(xiàn)瀑布流
    })
  },
  methods: {
    waterFall(
        wrapIdName,
        contentIdName,
        columns = 5,
        columnGap = 20,
        rowGap = 20
    ) {
      // 獲得內(nèi)容可用寬度(去除滾動(dòng)條寬度)
      const wrapContentWidth =
          document.querySelector(wrapIdName).offsetWidth;
 
      // 間隔空白區(qū)域
      const whiteArea = (columns - 1) * columnGap;
 
      // 得到每列寬度(也即每項(xiàng)內(nèi)容寬度)
      const contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
 
      // 得到內(nèi)容項(xiàng)集合
      const contentList = document.querySelectorAll(contentIdName);
 
      // 成行內(nèi)容項(xiàng)高度集合
      const lineConentHeightList = [];
 
      for (let i = 0; i < contentList.length; i++) {
        // 動(dòng)態(tài)設(shè)置內(nèi)容項(xiàng)寬度
        contentList[i].style.width = contentWidth + "px";
 
        // 獲取內(nèi)容項(xiàng)高度
        const height = contentList[i].clientHeight;
 
        if (i < columns) {
          // 第一行按序布局
          contentList[i].style.top = "0px";
          contentList[i].style.left = contentWidth * i + columnGap * i + "px";
 
          // 將行高push到數(shù)組
          lineConentHeightList.push(height);
        } else {
          // 其他行
          // 獲取數(shù)組最小的高度 和 對(duì)應(yīng)索引
          let minHeight = Math.min(...lineConentHeightList);
          let index = lineConentHeightList.findIndex(
              (listH) => listH === minHeight
          );
 
          contentList[i].style.top = minHeight + rowGap +"px";
          contentList[i].style.left = (contentWidth + columnGap) * index + "px";
 
          // 修改最小列的高度 最小列的高度 = 當(dāng)前自己的高度 + 拼接過(guò)來(lái)的高度 + 行間距
          lineConentHeightList[index] += height + rowGap;
        }
      }
    },
  },
};
</script>
 
<style scoped>
* {
  margin: 0;
  padding: 0;
}
/* 最外層大盒子 */
.tab-container {
  padding-top: 20px;
  position: relative;
}
/* 每個(gè)小盒子 */
.tab-container .tab-item {
  position: absolute;
  height: auto;
  border: 1px solid #ccc;
  box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
  background: white;
  /* 元素不能中斷顯示 */
  break-inside: avoid;
  text-align: center;
}
.tab-container .tab-item img {
  width: 100%;
  height: auto;
  display: block;
}
</style>

五.外層使用和懶加載

在使用這個(gè)插件的時(shí)候,有兩個(gè)問(wèn)題,就是因?yàn)閮?nèi)層是position: absolute;定位,不會(huì)撐開(kāi)外部的p,會(huì)導(dǎo)致外層盒模型不好布局,還有就是頁(yè)面下拉懶加載,那要怎么辦呢?

這里我給出我的處理方法

怎么使用Vue實(shí)現(xiàn)一個(gè)圖片水平瀑布流插件

整體代碼如下:

<template>
  <div>
    <div class="list-box" @scroll="scrollFun">
      <compList :pbList="pbList" ref="compList"></compList>
    </div>
  </div>
</template>
 
<script>
import compList from "@/pages/test/components/compList";
export default {
  name:'testList',
  components:{
    compList
  },
  data() {
    return {
      //瀑布流數(shù)據(jù)
      pbList: [
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        }
      ],
      addList:[
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        },
        {
          url: "https://img1.baidu.com/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",
        }
      ],
      bottomMain:true
    };
  },
  methods:{
    scrollFun(e) {
      const  offsetHeight= e.target.offsetHeight
      const  scrollHeight= e.target.scrollHeight
      const  scrollTop= e.target.scrollTop
      if((scrollHeight - (offsetHeight+scrollTop)) < 10){
        if(this.bottomMain){
          this.bottomMain = false
          this.addListDataFun()
        }
      }
    },
    addListDataFun(){
      this.$Spin.show({
        render: (h) => {
          return h('div', [
            h('Icon', {
              'class': 'demo-spin-icon-load',
              props: {
                type: 'ios-loading',
                size: 18
              }
            }),
            h('div', '數(shù)據(jù)更新中...')
          ])
        }
      });
      setTimeout(() => {
        this.pbList = this.pbList.concat(this.addList)
        this.bottomMain = true
        this.$nextTick(()=>{
          this.$refs.compList.waterFall("#tabContainer", ".tab-item")
          this.$Spin.hide()
        })
      },1000)
    }
  }
};
</script>
 
<style scoped>
.list-box{
  position: relative;
  width: 100%;
  height: calc(100vh - 240px);
  background: white;
  padding: 20px 30px 20px 20px;
  margin-top: 20px;
  box-sizing: border-box;
  overflow: auto;
}
</style>

下拉的核心代碼為:

scrollFun(e) {
  const  offsetHeight= e.target.offsetHeight
  const  scrollHeight= e.target.scrollHeight
  const  scrollTop= e.target.scrollTop
  if((scrollHeight - (offsetHeight+scrollTop)) < 10){
    if(this.bottomMain){
      this.bottomMain = false
      this.addListDataFun()
    }
  }
},
addListDataFun(){
  this.$Spin.show({
    render: (h) => {
      return h('div', [
        h('Icon', {
          'class': 'demo-spin-icon-load',
          props: {
            type: 'ios-loading',
            size: 18
          }
        }),
        h('div', '數(shù)據(jù)更新中...')
      ])
    }
  });
  setTimeout(() => {
    this.pbList = this.pbList.concat(this.addList)
    this.bottomMain = true
    this.$nextTick(()=>{
      this.$refs.compList.waterFall("#tabContainer", ".tab-item")
      this.$Spin.hide()
    })
  },1000)
}

這里使用的是iView-UI的全局加載事件,如果你要用其他的UI框架,也可以自行修改

讀到這里,這篇“怎么使用Vue實(shí)現(xiàn)一個(gè)圖片水平瀑布流插件”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

vue
AI