实现一个用于长列表展开收起的组件

应用场景

  • 当列表内容很长时,需要滑到最底部才能查看下一条数据,这时可以使用该组件的交互来优化使用体验。收起当前一条,继续展开查看下一条数据。

组件思路

判断同时满足以下两个条件时,悬浮展示收起按钮

  1. 展开的内容的盒子的左上角坐标的Y值即top + 收起按钮的高度 <= 当前浏览器窗口的可视区域高度clientHeight
  2. 展开的内容的盒子的右下角坐标的Y值即bottom > 当前浏览器窗口的可视区域高度clientHeight
javascript 复制代码
<template>
  <div class="collapse-item">
    <div class="source">
      <slot name="source"></slot>
    </div>
    <div class="collapse-panel">
      <div class="panel">
        <div
          class="panel-content"
          ref="panel-content"
          :style="{ '--panelMinHeight': minHeight + 'px', '--panelHeight': panelHeight }"
          :class="isExpanded ? 'active' : ''"
        >
          <slot></slot>
        </div>
        <div
          class="expand-btn height-96 flex-center"
          @click="togglePanel"
          :class="{ 'is-fixed': fixedControl }"
          v-if="isExpandShow"
        >
          <span class="color-main">{{ isExpanded ? "收起" : "展开" }}</span>
          <img
            src="@/assets/databoard/arrow-fold.png"
            class="expand-icon width-24 height-22 m-l-10"
            :class="!isExpanded ? '' : 'icon-reverse'"
          />
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "CollapseItem",
  props: {
    minHeight: {
      type: Number,
      default: 0
    },
    isExpandShow: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      isExpanded: false,
      panelHeight: 0, // 展开内容高度
      fixedControl: false, // 固定收起按钮控制
      scrollTop: 0 // 展开之前的滚动位置
    };
  },
  watch: {
    isExpanded(val) {
      if (!val) {
        this.fixedControl = false;
        this.removeScrollHandler();
        window.scrollTo({
          top: this.scrollTop
        });
        return;
      }
      const timer = setTimeout(() => {
        window.addEventListener("scroll", this.scrollHandler);
        this.scrollHandler();
        clearTimeout(timer);
      }, 100);
    }
  },
  methods: {
    // 切换展开收起状态
    togglePanel() {
      if (!this.panelHeight) {
        const panel = this.$refs["panel-content"];
        const panelHeight = panel.scrollHeight;
        this.panelHeight = panelHeight + "px";
      }
      this.isExpanded = !this.isExpanded;
      this.$nextTick(() => {
        this.scrollTop = document.documentElement.scrollTop;
      });
    },
    // 监听滚动事件,控制收起按钮展示和隐藏
    scrollHandler() {
      const { top, bottom } = this.$refs["panel-content"].getBoundingClientRect();
      const clientHeight = document.documentElement.clientHeight;
      this.fixedControl = bottom > clientHeight && top + 48 <= clientHeight;
    },
    // 移除滚动事件
    removeScrollHandler() {
      window.removeEventListener("scroll", this.scrollHandler);
    }
  },
  beforeDestroy() {
    this.removeScrollHandler();
  }
};
</script>
<style lang="less" scoped>
.collapse-panel {
  .panel-content {
    width: 100%;
    height: var(--panelMinHeight);
    overflow-y: hidden;
    overflow-x: visible;
    transition: height 0.2s;
  }

  .active {
    height: var(--panelHeight);
  }
  .expand-btn {
    .expand-icon {
      transition: all 0.2s;
    }
    .icon-reverse {
      transform: rotate(180deg);
    }
  }
  .is-fixed {
    position: fixed;
    bottom: 0px;
    background: #fff;
    width: 100%;
    left: 0;
    box-shadow: 0px -8px 24px 1px rgba(0, 0, 0, 0.1);
  }
}
</style>
相关推荐
Mintopia6 分钟前
Three.js 中的噪声与图形变换:一场数字世界的舞蹈
前端·javascript·three.js
Mintopia11 分钟前
计算机图形学漫游:从像素到游戏引擎的奇幻之旅
前端·javascript·计算机图形学
钢铁男儿12 分钟前
C#接口实现详解:从理论到实践,掌握面向对象编程的核心技巧
java·前端·c#
前端的日常33 分钟前
以下代码,那一部分运行快
前端
GeGarron34 分钟前
Drawing:专注高效画图,让每一次创作都值得被珍藏
前端
梨子同志34 分钟前
Vue v-model 指令详解
前端·vue.js
杨进军35 分钟前
简易实现 React 页面初次渲染
前端·react.js·前端框架
血舞之境36 分钟前
同名类引发问题:没见过世面导致遇见各种诡异的问题
前端
杨进军37 分钟前
实现 React 多个原生标签子节点渲染
前端·react.js·前端框架
前端的日常37 分钟前
AI 工具中,经常提到的 mcp 是什么,有哪些与前端方向结合的场景?
前端