瀑布流在移动端的应用及发展趋势

瀑布流布局在移动端设计中因其灵活性和高效性而被广泛采用。这种布局方式能够动态调整内容展示,适应不同屏幕尺寸,提升用户体验。

移动端瀑布流的设计特点

瀑布流设计在移动端表现出强大的适应性,能够根据用户行为实时加载内容。高度不一的元素排列方式节省了空间,同时增强了视觉吸引力。

技术实现的关键要素

实现高效瀑布流需要考虑性能优化和动态加载策略。通过懒加载和分页技术减少初始加载时间,确保流畅的用户体验。响应式设计确保在不同设备上保持一致的表现。

未来发展方向

随着移动设备性能的提升,瀑布流将进一步结合AI和个性化推荐算法。动态内容调整和交互式元素的加入将丰富用户体验,使其在移动端应用中持续占据重要地位。

主要实现动态列数的瀑布流可以自定义列数

瀑布流布局实现方法

该方法通过动态计算每个内容项的位置,实现自适应宽度的瀑布流布局。适用于图片、卡片等内容的展示。

方法参数说明

/**
 * @param { string } wrapIdName    容器id(或class)名称
 * @param { string } contentIdName 容器中内容项id(或class)名称
 * @param { number } column        容器中内容展示列数
 * @param { number } columnGap     列间隔距离
 * @param { number } rowGap        行间隔距离
 */

核心实现逻辑

计算容器可用宽度和每项宽度

let wrapContentWidth = document.getElementById('tabContainer').offsetWidth - 8
let whiteArea = (columns - 1) * columnGap
let contentWidth = parseInt((wrapContentWidth - whiteArea) / columns)

处理第一行元素的定位

contentList[i].style.top = 0
contentList[i].style.left = contentWidth * i + columnGap * i + "px"
lineConentHeightList.push(height)

处理后续行元素的定位(寻找最短列)

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"
lineConentHeightList[index] += height + rowGap

使用示例

// 2列布局,间隔16px
waterFall('#container', '.item', 2, 16, 16)

// 3列布局,间隔20px
waterFall('#gallery', '.photo', 3, 20, 20)

注意事项

  1. 容器需要设置相对定位(position: relative)
  2. 内容项需要设置绝对定位(position: absolute)
  3. 在窗口resize时需要重新调用该方法
  4. 新增内容时需要重新计算布局

响应式适配建议

可通过监听窗口变化动态调整列数:

window.addEventListener('resize', () => {
  const cols = window.innerWidth < 768 ? 2 : 4
  waterFall('#container', '.item', cols)
})

主要实现瀑布流的方法

/**
     * @param { string } wrapIdName    容器id(或class)名称
     * @param { string } contentIdName 容器中内容项id(或class)名称
     * @param { number } column        容器中内容展示列数 手机的话建议改为2
     * @param { number } columnGap     容器中 列 间隔距离 默认为20
     * @param { number } rowGap        容器中 行 间隔距离 默认为20
     */
    //瀑布流方法:通过拿到dom循环,给每一个dom添加对应的定位位置排列出瀑布流布局。
    //通过判断列的高度,来把下一个盒子放在最短的地方补上
    waterFall(
      wrapIdName,
      contentIdName,
      columns = 2,
      columnGap = 16,
      rowGap = 16
    ) {
      // 获得内容可用宽度(去除滚动条宽度)
      // let wrapContentWidth = document.querySelector(wrapIdName).offsetWidth - 8;
      let wrapContentWidth = document.getElementById('tabContainer').offsetWidth - 8
      // console.log(wrapContentWidth,'wrapContenstWidth');
      // 间隔空白区域
      let whiteArea = (columns - 1) * columnGap;
      // 得到每列宽度(也即每项内容宽度)
      let contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
      // 得到内容项集合
      let contentList = document.querySelectorAll(contentIdName);
      // 成行内容项高度集合
      let lineConentHeightList = [];
      for (let i = 0; i < contentList.length; i++) {
        // 动态设置内容项宽度
        contentList[i].style.width = contentWidth + "px";
        // 获取内容项高度
        let height = contentList[i].clientHeight;
        if (i < columns) {
          // 第一行按序布局
          contentList[i].style.top = 0;
          contentList[i].style.left = contentWidth * i + columnGap * i + "px";
          // 将行高push到数组
          lineConentHeightList.push(height);
          document.getElementById("ttrs").style.height = lineConentHeightList[0] + "px"
        } else {
          // 其他行
          // 获取数组最小的高度 和 对应索引
          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";
          // 修改最小列的高度 最小列的高度 = 当前自己的高度 + 拼接过来的高度 + 行间距
          lineConentHeightList[index] += height + rowGap;
        }
      }
      //拿到最大高度赋值给页面
      let max = Math.max.apply(null, lineConentHeightList) 
      document.getElementById("ttrs").style.height = max + "px"
    },
  }

附上所有源码,若有任何改进建议或合作想法,随时可以联系。共同学习,一起进步!

<template>
    <div id="ttrs" class="ttr">
      <div class="tab-container" id="tabContainer">
      <div class="tab-item" v-for="(item, index) in pbList" :key="index" @click="handleJumpDetail(item)">
        <img :src="Url" class="water_img">
      </div>
    </div>
    </div>
</template>

<script>
export default {
  props: {
    datalist: {
      type: Array,
      default: []
    },
    //图片数组长度
    commelength: {
      type:Number,
      default: 0,
    }
  },
  data() {
    return {
      //瀑布流数据
      pbList: [],
      newLength:0,//新的图片长度
      nowImgComLen:0,//加载完成的图片长度
    };
  },
  watch: {
    datalist: {
      deep: true,
      handler(newV,oldV) {
        this.newLength = newV.length;
        if(newV.length != 0){
          this.handleMountedImg();
        }
        this.pbList = newV
      }
    }
  },
  created() {
    
  },
  mounted() {
    this.pbList = [...this.datalist];
    this.newLength = this.pbList.length;
    this.handleMountedImg();

  },
  methods: {
    handleMountedImg() {
      this.$nextTick(() => {
        let imgLen = this.newLength - this.commelength
        for (let i = 0; i < imgLen; i++) {
          let imgList = document.querySelector(".water_img");
          imgList.onload = this.handlescroll();
        }
      })
    },
    
    handlescroll() {
      this.waterFall("#tabContainer", ".tab-item"); //实现瀑布流
      // 窗口变化自适应布局
      window.onresize = () => {
        return (() => {
          this.waterFall("#tabContainer", ".tab-item");
        })();
      };
    },
    /**
     * @param { string } wrapIdName    容器id(或class)名称
     * @param { string } contentIdName 容器中内容项id(或class)名称
     * @param { number } column        容器中内容展示列数 手机的话建议改为2
     * @param { number } columnGap     容器中 列 间隔距离 默认为20
     * @param { number } rowGap        容器中 行 间隔距离 默认为20
     */
    //瀑布流方法:通过拿到dom循环,给每一个dom添加对应的定位位置排列出瀑布流布局。
    //通过判断列的高度,来把下一个盒子放在最短的地方补上
    waterFall(
      wrapIdName,
      contentIdName,
      columns = 2,
      columnGap = 16,
      rowGap = 16
    ) {
      // 获得内容可用宽度(去除滚动条宽度)
      // let wrapContentWidth = document.querySelector(wrapIdName).offsetWidth - 8;
      let wrapContentWidth = document.getElementById('tabContainer').offsetWidth - 8
      // console.log(wrapContentWidth,'wrapContenstWidth');
      // 间隔空白区域
      let whiteArea = (columns - 1) * columnGap;
      // 得到每列宽度(也即每项内容宽度)
      let contentWidth = parseInt((wrapContentWidth - whiteArea) / columns);
      // 得到内容项集合
      let contentList = document.querySelectorAll(contentIdName);
      // 成行内容项高度集合
      let lineConentHeightList = [];
      for (let i = 0; i < contentList.length; i++) {
        // 动态设置内容项宽度
        contentList[i].style.width = contentWidth + "px";
        // 获取内容项高度
        let height = contentList[i].clientHeight;
        if (i < columns) {
          // 第一行按序布局
          contentList[i].style.top = 0;
          contentList[i].style.left = contentWidth * i + columnGap * i + "px";
          // 将行高push到数组
          lineConentHeightList.push(height);
          document.getElementById("ttrs").style.height = lineConentHeightList[0] + "px"
        } else {
          // 其他行
          // 获取数组最小的高度 和 对应索引
          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";
          // 修改最小列的高度 最小列的高度 = 当前自己的高度 + 拼接过来的高度 + 行间距
          lineConentHeightList[index] += height + rowGap;
        }
      }
      //拿到最大高度赋值给页面
      let max = Math.max.apply(null, lineConentHeightList) 
      document.getElementById("ttrs").style.height = max + "px"
    },
  }
};
</script>

<style lang="less" scoped>
.ttr {
  width: 100%;
}
.tab-container {
  width: 100%;
  background-color: #fff;
  height: 100%;
  position: relative;
  .tab-item {
    margin-bottom: 16px;
    position: absolute;
    height: auto;
    border-radius: 20px;
    border: 1px solid #e5e6eb;
    background: #fff;
    /* 元素不能中断显示 */
    break-inside: avoid;
    img {
      max-width: 320px;
      width: 100%;
      height: auto;
      border-radius: 12px;
    }
  }
}
</style>

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐