其他分享
首页 > 其他分享> > kl-waterfall 瀑布流

kl-waterfall 瀑布流

作者:互联网

文章目录

使用

  <kl-waterfall
    @touchBottom="touchBottom"
    :distant="50"
    :cope="4"
    :margin="10"
    :sleep="200"
  >
    <kl-waterfall-item v-for="(item, index) in state.imgs" :key="index" border>
      <div class="item">
        <img :src="item.url" alt="" />
        <p>
          {{ item.content }}
        </p>
      </div>
    </kl-waterfall-item>
  </kl-waterfall>

实现

waterfall index文件

<template>
  <div class="kl-waterfall" ref="waterRef">
    <slot></slot>
  </div>
</template>
<script lang="ts" setup>
import {
  onMounted,
  reactive,
  ref,
  onBeforeUpdate,
  onBeforeUnmount,
} from "vue-demi";

import { throttle } from "../../utils/tool";

const props = defineProps({
  cope: {
    // 份数
    type: Number,
    default: 5,
    required: false,
  },
  margin: {
    // 每份的间隔
    type: Number,
    default: 10,
    required: false,
  },
  sleep: {
    // 默认延时加载 建议图片越多,值越大
    type: Number,
    default: 200,
    required: false,
  },
  distant: {
    // 距离多远触底 就触发 触底回调
    type: Number,
    default: 200,
    required: false,
  },
});

const emits = defineEmits(["touchBottom"]);

const waterRef = ref();

interface i_state {
  childWidth: number; // 每项的公共宽度
  heightArr: number[]; // 存储每列的高度数组
  max: number; // 当前的最高项的高度
  top: number; // 当前kl-waterfall 距离顶部的距离
  clientHeight: number; // 可视区高度
  isBottom: boolean; // 是否到了底部
  lastLength: number; // 上次的长度
  timer: any; // 记录timer
}
const state = reactive<i_state>({
  childWidth: 0,
  heightArr: [],
  max: 0,
  top: 0,
  clientHeight: 0,
  isBottom: false,
  lastLength: 0,
  timer: null,
});

// 滚动事件
const handleScroll = throttle(() => {
  //scrollTop就是触发滚轮事件时滚轮的高度
  var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
  // console.log(scrollTop + state.clientHeight);

  if (
    state.max + state.top <= scrollTop + state.clientHeight + props.distant &&
    !state.isBottom
  ) {
    state.isBottom = true;
    emits("touchBottom", {
      code: 200,
      msg: "触底了",
    });
  }
}, 50);

onMounted(() => {
  const width = waterRef.value.clientWidth;
  state.top = waterRef.value.offsetTop;
  state.clientHeight = document.documentElement.clientHeight;

  // 每个子元素的宽度
  let childWidth = Math.ceil(
    (width - (props.cope + 1) * props.margin) / props.cope
  );

  state.childWidth = childWidth;

  // 开始处理业务
  handleEvent();

  // 添加触底事件
  window.addEventListener("scroll", handleScroll, true);
});

onBeforeUnmount(() => {
  clearTimeout(state.timer);
  state.timer = null;
});

// 具体处理相关的业务
function handleEvent() {
  // console.log(state.lastLength);
  clearTimeout(state.timer);
  state.timer = null;

  state.timer = setTimeout(() => {
    // 给每个子项设宽
    let childs = waterRef.value.children;
    // console.log(childs.length);

    let childLength = childs.length;

    for (let i = state.lastLength; i < childLength; i++) {
      childs[i].style.width = state.childWidth + "px";
    }
    state.isBottom = false;
    // 获取每个元素的高
    for (let i = state.lastLength; i < childLength; i++) {
      let height: number = Number(childs[i].offsetHeight) || 0;
      // console.log(height);

      // 第一排设置
      if (i < props.cope) {
        childs[i].style.top = props.margin + "px";
        childs[i].style.left =
          (i + 1) * props.margin + i * state.childWidth + "px";
        state.heightArr.push(props.margin + height);
        // console.log("kkk");
      } else {
        // 找到最小项
        let minHeight = Math.min(...state.heightArr);

        // 找到最小项的index
        let minHeightIndex = state.heightArr.findIndex((item) => {
          return item == minHeight;
        });

        childs[i].style.top =
          state.heightArr[minHeightIndex] + props.margin + "px";
        childs[i].style.left =
          (minHeightIndex + 1) * props.margin +
          minHeightIndex * state.childWidth +
          "px";
        state.heightArr[minHeightIndex] = minHeight + props.margin + height;
      }
    }

    // 重新给父元素定高度
    let max = Math.max(...state.heightArr);
    state.max = max;
    waterRef.value.style.height = max + props.margin + "px";
    // 记录下当前的长度

    state.lastLength = childLength;
  }, props.sleep);
}

// 当数据更新时的业务
onBeforeUpdate(() => {
  handleEvent();
});
</script>
<style lang="scss" scoped>
.kl-waterfall {
  display: block;
  position: relative;
}

.kl-loading {
  height: 50px;
  text-align: center;
  color: #666;
}
</style>

kl-waterfall-item

<template>
  <div
    :class="[
      'kl-clearFix',
      'kl-waterfall-item',
      border ? 'kl-waterfall-item-border' : '',
    ]"
  >
    <slot />
  </div>
</template>
<script lang="ts" setup>
defineProps({
  border: {
    type: Boolean,
    default: false,
    required: false,
  },
});
</script>

<style lang="scss" scoped>
.kl-waterfall-item-border {
  box-shadow: 0 0 5px #555;
  display: block;
  position: absolute;
}
</style>

标签:childs,state,kl,瀑布,waterfall,let,props,heightArr,margin
来源: https://blog.csdn.net/qq_42451776/article/details/120642864