1. 程式人生 > >Vue 原生瀑布流圖片

Vue 原生瀑布流圖片

效果

  

 思路

1、html中建立四個“管道”

2、後臺獲取到資料

3、遍歷資料,獲取高度最小的管道,填入資料

4、滾動條到底時觸發函式,獲取下一頁資料,然後再執行步驟3

參考

jquery實現滑動到底部載入下一頁的資料

程式碼

template:建立四個piping,ref引數是為了在js中獲取dom元素。

<template>
    <div class="friend_moments">
      <div class="water">
        <div class="piping" ref="piping0">
        </div>
        <div class="piping" ref="piping1">
        </div>
        <div class="piping" ref="piping2">
        </div>
        <div class="piping" ref="piping3">
        </div>
      </div>
    </div>
</template>

 script:

export default {
  data() {
    return {
      moments: [],
      available: 1,
      height1: 0,
      height2: 0,
      height3: 0,
      page: 1
    };
  },
  created() {
    // 獲取第一頁資料
    this.fetchMoments();
  },
  mounted() {
    // 用來監聽滾輪
    window.addEventListener("scroll", this.handleScroll);
  },
  methods: {
    fetchMoments() {
      // 請求介面方法
      fetch("api/moments")
        .then(res => res.json())
        .then(res => {
          this.moments = res.data;
          // 分配資料到指定管道
          this.sort(0);
        });
    },
    // sort()函式是遞迴的,因為要確保每個卡片的圖片載入完成後再獲取管道的高度,但是圖片載入完成的函式是個非同步函式,
    // 如果放在for迴圈中會打亂順序,因此要使非同步函式同步執行,for迴圈改為遞迴。
    sort(j) {
      if (j < this.moments.length) {
        let that = this;
        // 建立Image類
        var newImg = new Image();
        // 獲取要載入的圖片地址
        newImg.src =
          "http://lanyue.ink:8123/images/" +
          (Math.floor(Math.random() * 15) + 1) +
          ".png";
        // 圖片載入完成後(非同步)
        newImg.onload = () => {
          // 四個管道的高度 
          var arr = [
            that.$refs.piping0.offsetHeight,
            that.$refs.piping1.offsetHeight,
            that.$refs.piping2.offsetHeight,
            that.$refs.piping3.offsetHeight
          ];
          //獲取管道最小高度
          var min = arr.indexOf(Math.min.apply(Math, arr));
          // 新增卡片的模板
          var html =
            `<div class="card">
                <img src=` + newImg.src + `>
                <div>
                  <img src="http://lanyue.ink:8123/images/avatar.jpg" alt="">
                  <div>` + this.moments[j].id + "  " + this.moments[j].content + `</div>
                </div>
            </div>`;
          //給最小的管道新增卡片
          if (min == 0) {
            that.$refs.piping0.innerHTML += html;
          } else if (min == 1) {
            that.$refs.piping1.innerHTML += html;
          } else if (min == 2) {
            that.$refs.piping2.innerHTML += html;
          } else if (min == 3) {
            that.$refs.piping3.innerHTML += html;
          }
          that.sort(j + 1);
        };
      }
    },
    handleScroll() {
      // 獲取滾輪位置
      var scrollTop =
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop;
      this.height1 = scrollTop;
      // 文件高度
      this.height2 = document.body.scrollHeight;
      // 可視區域
      this.height3 =
        document.compatMode == "CSS1Compat"
          ? document.documentElement.clientHeight
          : document.body.clientHeight;
      // 如果滾動到最低(這裡設定離最底還有100距離才觸發函式)
      // available條件是為了防止觸底時一直不斷地請求。因此,請求一次後available設為0,直到滾動到離底部超過100距離(即資料載入玩後)才設為1
      if (this.height3 + this.height1 >= this.height2 - 100 && this.available) {
        //請求下一頁
        this.page++;
        this.available = 0;
        let that = this;
        fetch("api/moments?page=" + this.page)
          .then(res => res.json())
          .then(res => {
            that.moments = res.data;
            if (that.moments[0]) {
              that.sort(0);
            } else {
              that.page--;
            }
          });
      } else if (this.height3 + this.height1 < this.height2 - 100) {
        this.available = 1;
      }
    }
  }
};

scss:

<style lang="scss">
.friend_moments {
  width: 100%;
  display: flex;
  justify-content: center;
}
.a {
  position: fixed;
  width: 200px;
  top: 200px;
  right: 100px;
  z-index: 10;
  background: #eee;
}
.water {
  width: 1240px;
  margin-top: 60px;
  display: flex;
  align-items: flex-start;
  margin-bottom: 100px;
  .piping {
    width: 25%;
    padding: 10px;
    padding-bottom: 0px;
  }
}
.card {
  width: 290px;
  border-radius: 5px;
  box-shadow: 0px 0px 5px #888888;
  margin-bottom: 20px;
  > img:first-child {
    width: 100%;
    border-radius: 5px;
  }
  > div:nth-child(2) {
    display: flex;
    align-items: center;
    padding: 10px;
    > img {
      border-radius: 100%;
      width: 32px;
      height: 32px;
      margin-right: 10px;
    }
  }
}
</style>

擴充套件

如果希望圖片框大小一樣,則設定圖片為背景圖片,cover居中顯示。

1、就把scss中的card類改為:

.card {
  width: 290px;
  border-radius: 5px;
  box-shadow: 0px 0px 5px #888888;
  margin-bottom: 20px;
  > div:first-child {
    width: 100%;
    height: 200px;
    background-repeat: no-repeat;
    background-position: center;
    background-size: cover;
    border-radius: 5px;
  }
  > div:nth-child(2) {
    display: flex;
    align-items: center;
    padding: 10px;
    > img {
      border-radius: 100%;
      width: 32px;
      height: 32px;
      margin-right: 10px;
    }
  }
}

2、把script中html模板改成:

var html =
    `<div class="card">
        <div style=background-image:url(` + newImg.src + `)></div>
        <div>
            <img src="http://lanyue.ink:8123/images/avatar.jpg" alt="">
            <div>` + this.moments[j].id + "  " + this.moments[j].content + `</div>
        </div>
    </div>`;

修改後的效果:

這樣,高度的差別就主要來自卡片的底部內容高度。