vue+uniapp瀑布流布局怎么实现?

这篇“vue+uniapp瀑布流布局怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue+uniapp瀑布流布局怎么实现”文章吧。

一、实现原理

通过动态计算哪一列高度最低,就把图片放置该列下显示,直至所有图片分列完毕

计算哪一列高度最低具体实现过程又分2种方式:

方式1:通过计算每一列每张图片渲染后高度进行累加就是该列的高度,记录每列累加高度比较大小

方式2:直接通过图片父级元素高度(列div)来判断哪一列最低

区别:方式1无需等待图片真实渲染完成在比较高度,方式2需要等待图片真实渲染完成在获取高度

二、代码实现

以左右2列为例

<template>
  <div class="page">
    <!-- 左图片列表 -->
    <div class="left" ref="left">
      <img
        class="img"
        v-for="(item, index) in leftList"
        :key="index"
        :src="item"
      />
    </div>
    <!-- 右图片列表 -->
    <div class="right" ref="right">
      <img
        class="img"
        v-for="(item, index) in rightList"
        :key="index"
        :src="item"
      />
    </div>
  </div>
</template>
<style scoped>
.page {
  width: 100%;
  display: flex;
  align-items: flex-start;
  padding: 0 1%;
  box-sizing: border-box;
}
.left,
.right {
  margin: 0 auto;
  width: 48%;
}
.img {
  width: 100%;
  height: auto;
  margin-bottom: 10px;
}
</style>

1.方式1(图片高度累加比较法)

<script>
export default {
  data() {
    return {
      imgList: [
        "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
        "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
        "https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
        "https://img1.baidu.com/it/u=3866290852,3566512524&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500",
        "https://img2.baidu.com/it/u=1114729443,1120710416&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500",
        "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
        "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
        "https://img2.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
      ], //所有图片
      leftList: [], //左边列图片
      rightList: [], //右边列图片
      leftHeight: 0, //左边列高度
      rightHeight: 0, //右边列高度
      columnWidth: 0, //列宽度
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.columnWidth = this.$refs.left.clientWidth;
      this.setWaterFallLayout();
    });
  },
  methods: {
    //方法1
    async setWaterFallLayout() {
      for (let item of this.imgList) {
        let img = new Image();
        img.src = item;
        try{
          let h = await this.getImgHeight(img);//图片渲染后高度
          if (this.leftHeight <= this.rightHeight) {//左边列比右边低,图片放入左边
            this.leftList.push(item);
            this.leftHeight += h;
          } else {//否则,图片放入右边
            this.rightList.push(item);
            this.rightHeight += h;
          }
        }catch(e){
          console.log(e)
        }
      }
    },
    //获取图片高度
    getImgHeight(img) {
      return new Promise((resolve,reject) => {
      //图片加载完成
        img.onload = () => {
          let h = (img.height / img.width) * this.columnWidth;//计算图片渲染后高度
          resolve(h);
        };
        //加载出错
        img.onerror=()=>{
          reject('error')
        }
      });
    },
  },
};
</script>

2.方式2(父元素高度比较法)

每次放入图片需要等待渲染后再重新计算父元素高度,关键代码 await this.$nextTick()

<script>
export default {
  data() {
    return {
      imgList: [
        "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
        "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
        "https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
        "https://img1.baidu.com/it/u=3866290852,3566512524&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500",
        "https://img2.baidu.com/it/u=1114729443,1120710416&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500",
        "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
        "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
        "https://img2.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
      ], //所有图片
      leftList: [], //左边列表图片
      rightList: [], //右边列表图片
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.setWaterFallLayout2();
    });
  },
  methods: {
    //方法2
    async setWaterFallLayout2() {
      for (let item of this.imgList) {
        if (this.$refs.left.clientHeight <= this.$refs.right.clientHeight) {//左边列比右边低,图片放入左边
          this.leftList.push(item);
        } else {//否则图片放入右边
          this.rightList.push(item);
        }
        await this.$nextTick();//等待渲染完成后重新比较左右高度
      }
    },
  },
};
</script>

三.uniapp实现

由于uniapp获取元素高度和vue有所区别,造成实现瀑布流方式也需要调整。我们知道uniapp不能通过this.$ref.xx.clientHeight获取元素高度,而需要通过uni.createSelectorQuery().in(this).select(&lsquo;.xxxx&rsquo;).boundingClientRect().exec()来获取,且经过实测当图片动态加入列后通过该api计算出父元素真实高度是不准确的,所以uniapp瀑布流布局实现方式只能通过方法1(也即图片高度累加法)进行实现,除了上面方法1通过img.onload来获取图片高度外,uniapp还提供uni.getImageInfo方法来更方便获取图片高度。

代码实现

<template>
        <view class="page">
                <view class="left" ref="left">
                        <image class="image" v-for="(item,i) in leftList" :key="i" :src="item" mode="widthFix"></image>
                </view>
                <view class="right" ref="right">
                        <image class="image" v-for="(item,i) in rightList" :key="i" :src="item" mode="widthFix"></image>
                </view>
        </view>
</template>
<style >
        .page {
                width: 100%;
                display: flex;
                align-items: flex-start;
                padding: 0 1%;
                box-sizing: border-box;
        }

        .left,
        .right {
                margin: 0 auto;
                width: 48%;
        }

        .image {
                width: 100%;
                height: auto;
                margin-bottom: 10px;
        }
</style>
<script>
        export default {
                data() {
                        return {
                                imageList: [
                                        "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
                                        "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
                                        "https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
                                        "https://img1.baidu.com/it/u=3866290852,3566512524&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500",
                                        "https://img2.baidu.com/it/u=1114729443,1120710416&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500",
                                        "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
                                        "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
                                        "https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
                                ], //所有图片
                                leftList: [], //左边列图片
                                rightList: [], //右边列图片
                                leftHeight: 0, //左边列高度
                                rightHeight: 0, //右边列高度
                                columnWidth: 0 //列宽度
                        }
                },
                mounted() {
                        this.$nextTick(() => {
                                uni.createSelectorQuery().in(this).select('.left').boundingClientRect(res => {
                                        this.columnWidth = res.width
                                        //方法1
                                        this.setWaterFallLayout()
                                        //方法2
                                        // this.setWaterFallLayout2()
                                }).exec()
                        })
                },
                methods: {
                        //方法1通过img.onload
                        async setWaterFallLayout() {
                                for (let item of this.imageList) {
                                        let img = new Image()
                                        img.src = item
                                        try {
                                                let h = await this.getImgHeight(img)
                                                if (this.leftHeight <= this.rightHeight) {
                                                        this.leftList.push(item)
                                                        this.leftHeight += h
                                                } else {
                                                        this.rightList.push(item)
                                                        this.rightHeight += h
                                                }
                                        } catch (e) {
                                                console.log(e)
                                        }

                                }

                        },
                        //获取图片高度
                        getImgHeight(img) {
                                return new Promise((resolve, reject) => {
                                        img.onload = () => {
                                                let h = img.height / img.width * this.columnWidth
                                                resolve(h)
                                        }
                                        //加载出错
                                        img.onerror = () => {
                                                reject('error')
                                        }
                                })
                        },
                        //方法2通过uni.getImageInfo
                        async setWaterFallLayout2() {
                                for (let item of this.imageList) {
                                        uni.getImageInfo({
                                                src: item,
                                                success: e => {
                                                        if (this.leftHeight <= this.rightHeight) {
                                                                this.leftList.push(item)
                                                                this.leftHeight += e.height
                                                        } else {
                                                                this.rightList.push(item)
                                                                this.rightHeight += e.height
                                                        }

                                                }
                                        })
                                }
                        }

                },

        }
</script>

四、多列实现

多列实现和2列一样,动态生成每列图片数据和记录每列高度

代码实现

以最简单的父元素高度比较法(方式2)为例实现,图片高度累加比较法(方式1)自行类比实现

<template>
  <div class="page">
    <div
      class="column"
      ref="column"
      v-for="(item, index) in columnList"
      :key="index"
    >
      <img class="img" v-for="(n, i) in item" :key="i" :src="n" />
    </div>
  </div>
</template>
<style scoped>
.page {
  width: 100%;
  display: flex;
  align-items: flex-start;
  padding: 0 1%;
  box-sizing: border-box;
}

.column {
  flex: 1;
  padding: 0 10px;
  box-sizing: border-box;
  width: 0;
}

.img {
  width: 100%;
  height: auto;
  margin-bottom: 10px;
}
</style>
<script>
export default {
  data() {
    return {
      imgList: [
        "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
                                        "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
                                        "https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
                                        "https://img1.baidu.com/it/u=3866290852,3566512524&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500",
                                        "https://img2.baidu.com/it/u=1114729443,1120710416&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500",
                                        "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
                                        "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
                                        "https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&fmt=auto&app=138&f=JPEG?w=335&h=500",
                                        "https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082",
                                        "https://img2.baidu.com/it/u=1893470775,4143435497&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
      ], //所有图片
      columnList: [], //分配后的每列图片
      columWidth: 0, //每列宽度
      columnCount: 5, //显示几列
    };
  },
  created() {
    //初始化数据
    for (let i = 0; i < this.columnCount; i++) {
      this.columnList.push([]);//生成每列图片数组
    }
  },
  mounted() {
    this.$nextTick(()=>{
      this.setWaterFallLayout();
    })
  },
  methods: {
   //瀑布布局
    async setWaterFallLayout() {
      for (let item of this.imgList) {
        let columnHeight = this.$refs.column.map((item) => item.clientHeight); //每列高度数组
        let min = Math.min(...columnHeight); //找出最小高度值
        let index = columnHeight.findIndex((item) => item === min); //找出最小高度列的索引
        this.columnList[index].push(item);//放入图片
        await this.$nextTick(); //等待渲染完成后重新比较高度
      }
    },
  },
};
</script>

以上就是关于“vue+uniapp瀑布流布局怎么实现”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注***行业资讯频道。