结合vue实现移动端页面的瀑布流布局

参考博客:浅谈瀑布流原理及Vue实现
结合自己项目的需求进行修改

基本原理:多行等宽不等高元素排列。第一排图片的顶部会处于同一个高度,依次排列在顶端,第二行以及之后的行则利用定位在最短的一列下面进行排列

// 封装了WaterFallFlow.vue子组件
<template>
  <div class="waterfall_container">
    <div
      v-for="(item, index) in waterfallList"
      :key="index"
      class="waterfall_item"
      :style="{ top: item.top + 'px', left: item.left + 'px', width: imgWidth + 'px', height: item.itemHeight }"
    >
      <div class="coverImg">
        <Cover :src="item.src" alt="" :height="item.imgHeightPercent + '%'"></Cover>
      </div>
      <div class="content">
        <div class="title line2">528㎡让人惊艳的混搭魅力</div>
        <div class="info flex flex-between flex-middle">
          <div class="designer flex-item-3">
            <img src="@/assets/images/灵感详情.png" alt="" />
            <span class="name">FFSTUDsssssssssssIO</span>
          </div>
          <div class="watcher flex-item-2">
            <img src="@/assets/images/icon/列表-浏览量.png" alt="" />
            <span> 3.5k</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Cover from "@/components/Cover";
export default {
  components: {
    Cover,
  },
  props: {
  // 父组件传值给子组件
    list: {
      type: Array,
      default: function() {
        return [
        // 在js中存放静态文件链接的时候要用require,不然不会显示
          require("@/assets/images/灵感列表2.png"),
          require("@/assets/images/产品目录3.png"),
          require("@/assets/images/灵感详情.png"),
          require("@/assets/images/灵感列表1.png"),
          require("@/assets/images/产品目录2.png"),
          require("@/assets/images/产品目录1.png"),
          require("@/assets/images/国际趋势2品牌1.png"),
          require("@/assets/images/国际趋势2品牌2.png"),
          require("@/assets/images/国际趋势2品牌3.png"),
          require("@/assets/images/国际趋势1品牌3.png"),
          require("@/assets/images/国际趋势1品牌2.png"),
          require("@/assets/images/设计师列表-作品3.png"),
          require("@/assets/images/设计师列表-作品2.png"),
        ];
      },
    },
  },
  data() {
    return {
      waterfallList: [],
      imgWidth: 0, // 图片宽度
      contentHeight: 100, // 内容高度
      waterfallCol: 2, // 瀑布流列数
      itemRight: 0.5, // 图片右边距(以rem为单位)
      itemBottom: 1, // 图片下边距(以rem为单位)
      waterfallDeviationHeight: [], // 瀑布流高度偏移量
    };
  },
  mounted() {
    // 根元素像素
    const rootElePixel = parseInt(window.getComputedStyle(document.querySelector("html"), null).fontSize);
    this.itemRight *= rootElePixel;
    this.itemBottom *= rootElePixel;
    this.calculationValue();
  },
  methods: {
    // 计算每项的宽度(即图片/内容宽度)
    calculationValue() {
      const containerWidth = document.querySelector(".waterfall_container").offsetWidth;
      this.imgWidth = (containerWidth / this.waterfallCol) - this.itemRight;
      // 初始化偏移高度数组,该数组用于存放每一列的高度
      this.waterfallDeviationHeight = new Array(this.waterfallCol);
      for (let i = 0; i < this.waterfallDeviationHeight.length; i++) {
        this.waterfallDeviationHeight[i] = 0;
      }
      this.imgPreloading();
    },
    // 图片预加载
    imgPreloading() {
      for (let i = 0; i < this.list.length; i++) {
        const aImg = new Image();
        aImg.src = this.list[i];
        // 注意:图片加载完成的顺序不一样的,所以在页面显示图片的顺序每次都可能不一样
        aImg.onload = aImg.onerror = (e) => {
          const itemData = {};
          // 图片高度按比例缩放
          const imgHeight = (this.imgWidth / aImg.width) * aImg.height;
          // 获取图片高度比
          itemData.imgHeightPercent = (imgHeight / this.imgWidth) * 100;
          // 整体高度 = 图片高度 + 内容高度
          itemData.height = imgHeight + this.contentHeight;
          itemData.src = this.list[i];
          // 将每一项都push到一个列表中
          this.waterfallList.push(itemData);
          // 进行瀑布流布局
          this.waterfallFlowLayout(itemData);
        };
      }
    },
    // 瀑布流布局
    waterfallFlowLayout(itemData) {
      const shortestIndex = this.getShortestCol();
      itemData.top = this.waterfallDeviationHeight[shortestIndex];
      itemData.left = shortestIndex * (this.itemRight + this.imgWidth);
      this.waterfallDeviationHeight[shortestIndex] += itemData.height + this.itemBottom;
    },
    /**
     * 找到最短的列并返回索引
     * @returns {number} 索引
     */
    getShortestCol() {
      const shortest = Math.min.apply(null, this.waterfallDeviationHeight);
      return this.waterfallDeviationHeight.indexOf(shortest);
    },
  },
};
</script>

<style lang="less" scoped>
.waterfall_container {
  width: 100%;
  height: 100%;
  position: relative;
  .waterfall_item {
    float: left;
    position: absolute;
    // 底边阴影
    box-shadow: 0px 0.2rem 0.1rem #f7f8fa;
    .content {
      width: 100%;
      padding: 0.4rem;
      .title {
        font-size: 0.8rem;
      }
      .info {
        margin: 0.4rem 0;
        font-size: 0.5rem;
        color: #bbb;
        .designer {
          margin-right: 0.3rem;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
          img {
            border-radius: 50%;
            width: 1.6rem;
            height: 1.6rem;
          }
        }
        .watcher {
          img {
            width: 1rem;
            height: 1rem;
          }
        }
      }
    }
  }
  img {
    vertical-align: middle;
  }
}
</style>

// 封面图Cover.vue子组件
<template>
  <div>
    <div class="cover-img">
      <img :src="src" :alt="alt" />
    </div>
    <!-- 控制封面图高度 -->
    <div class="holder" :style="'padding-bottom:' + height"></div>
  </div>
</template>

<script>
export default {
  props: {
    src: {
      type: String,
      required: true,
    },
    alt: {
      type: String,
      default: "",
    },
    height: {
      type: String,
      default: "100%",
    },
  },
  data() {
    return {};
  },
};
</script>

<style lang="less" scoped>
.cover-img {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  overflow: hidden;
  background-position: 50%;
  background-size: cover;
}
.cover-img img {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 50%;
  height: 100%;
  transform: translateX(-50%);
}
.holder {
  width: 100%;
  position: relative;
  padding-bottom: 100%;
  background-repeat: no-repeat;
  background-size: cover;
}
</style>



效果图:
在这里插入图片描述

在这里插入图片描述

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Vue实现移动端小米应用商城页面需要进行以下步骤: 1. 界面设计:根据小米应用商城的界面设计需求,使用Vue框架中的组件库和样式预处理器进行页面的布局和设计。可以使用小米官方提供的UI组件库或者其他第三方组件库来提高开发效率和界面质量。 2. 路由设置:使用Vue Router来设置页面的路由。根据小米应用商城的功能需求,在路由配置中设置每一个页面对应的路由路径和组件,实现页面之间的切换和导航。 3. 数据获取和展示:使用Vue的响应式数据绑定功能,结合小米应用商城的数据接口,通过网络请求获取数据,并将数据进行展示。可以使用Vue的生命周期钩子函数来进行数据的初始化和更新。 4. 搜索和筛选:实现小米应用商城中的搜索和筛选功能。使用Vue实现搜索框的输入和结果展示的实时更新,并根据用户的筛选条件实时显示相应的应用列表。 5. 购物车功能:实现小米应用商城中的购物车功能。使用Vue的状态管理库(如Vuex)来管理购物车中的商品信息,实现商品的添加、删除、数量修改等操作,并能够计算出购物车中商品的总价。 6. 移动适配:使用Vue的移动适配方案(如vw/vh、rem)来实现页面的适配和响应式布局。根据不同的屏幕尺寸和设备类型,设置相应的样式规则,以确保页面在不同设备上能够良好展示。 7. 动画和交互:使用Vue的过渡效果和动画库来实现页面切换、菜单展开等动画效果,提升用户体验。同时,根据小米应用商城的交互功能需求,合理利用Vue的指令、事件绑定等特性来实现用户的点击、滑动、拖拽等交互操作。 通过以上步骤,可以基于Vue框架实现移动端小米应用商城页面,并达到用户友好、界面美观和功能完备的目标。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值