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

应用场景

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

组件思路

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

  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>
相关推荐
码视野31 分钟前
基于 Spring Boot + Vue3 的【大学英语四六级 (CET-4/6) 作文智能评分与句式润色系统】设计与实现(含PRD/三端高保真源码/大屏)
java·前端·人工智能·spring boot·后端·vue3
大模型码小白36 分钟前
AI 对话流性能调优:万级消息的虚拟滚动落地
java·大数据·前端·javascript·人工智能·算法·机器学习
xm_xm_xm_11 小时前
react17版本以前类组件常用操作
前端·javascript·react.js
ly76892 小时前
Web 性能优化实战:从 Core Web Vitals 到工程化性能治理
前端·性能优化
冬奇Lab3 小时前
一天一个开源项目(第200篇):next-forge - 生产级 Next.js SaaS 启动模板
前端·前端框架·开源
Profile排查笔记3 小时前
指纹浏览器怎么设置 IP?代理配置、检测与排错流程
前端·人工智能·后端·自动化
lzhdim3 小时前
13、JavaScript事件循环机制 - JavaScript学习系列文章
开发语言·前端·javascript·学习·ecmascript
liangshanbo12153 小时前
Vue 3 <script setup> 的本质原理是什么?它与普通 setup()有何区别?
前端·javascript·vue.js
IT_陈寒4 小时前
SpringBoot自动配置失效?你可能漏了这个小开关
前端·人工智能·后端
kyriewen4 小时前
我写了1个复盘Skill,和AI协作踩过的坑第二天自动变成护栏
前端·程序员·ai编程