创建多行文本展开折叠组件

646 阅读6分钟

先看下效果

PixPin_2024-07-02_11-09-37

组件库查看 组件地址

创建一个vue文件

编写基本结构

<script setup name="collapseToExpand" lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
​
const props = defineProps({
  /*  text: {
    type: String,
    default: ''
  }, */
  row: {
    type: Number,
    default: 2,
  },
});
​
let text = ref(
  "生活有五颜六色,但并不是十全十美,所以我们需要从生活中汲取一些人生营养,于是乎阅读就成为了一部分人的生活常态。阅读这个东西对于人生来说似乎是一件必不可少的事,任由书上的情节来摆布我们的喜怒哀惧,或许一个不经意间的话语就成为我们刻骨铭心的深刻阅读记忆。无知的我们总被生活所敷衍,就像小时候的我们只爱吃甜的一样,但一个道理永远说服不了所有人,甜点也并不是最好吃的。在深入之后才明白阅读并不是容颜的羞花闭月、举止的温文尔雅、也不是财富的腰缠万贯、更不是权力的叱咤风云,而是一棵向阳性的草,在整个季节里开满了盛状的花。",
);
// 组件ref
const refMoreText = ref();
// 内容ref
const refContent = ref();
// 标识ref
const refTrigger = ref();
// 是否展开
const isExceed = ref(false);
// 是否显示更多
const moreVisible = ref(false);
</script>
​
<template>
  <div ref="refMoreText" class="more-text">
    <div ref="refContent" class="content">{{ text }}</div>
    <div
      ref="refTrigger"
      class="trigger text-primary bg"
      @click="handleTrigger"
      v-if="isExceed"
    >
      <span>{{ moreVisible ? "收起" : "... 展开更多" }}</span>
    </div>
  </div>
</template>
<style lang="scss" scoped></style>
​

接下来获取一开始显示的高度(正常显示的高度)、文本的行高

onMounted(() => {
  // 获取一开始的高度
  fullHeight = refContent.value.clientHeight;
  //  获取行高
  lineHeight = Number(window.getComputedStyle(refMoreText.value).lineHeight.slice(0, -2));
​
  // 折叠的高度
  textHeight = lineHeight * props.row;
​
});
  • window.getComputedStyle()方法获取指定DOM元素的当前的样式,这意味着它返回的是浏览器实际应用到元素上的CSS样式,包括所有CSS规则(内联样式、外部样式表和浏览器默认样式)的最终结果。

一开始应该是折叠的高度,开始写css样式

.more-text {
  position: relative;
  // 超出隐藏
  overflow: hidden;
  // 高度
  height: var(--height);
  // 换行类型
  word-break: break-all;
  // 空白处理
  white-space: pre-wrap;
  // 添加动画 改变的是高度
  transition: height 0.3s;
  // 内容设为absolute,避免高度被父元素影响
  .content {
    position: absolute;
  }
  // 设置标记的样式
  .trigger {
    position: absolute;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    // 设置高度和内容一样
    height: var(--lineHeight);
    line-height: var(--lineHeight);
    padding-left: 120px;
    cursor: pointer;
  }
  .bg {
    background: linear-gradient(
      to right,
      rgb(255, 255, 255, 0) 0,
      rgb(255, 255, 255, 1) 60%,
      #fff 100%
    );
  }
}
  • word-break: break-all;

    • word-break: break-all; 属性用于规定如何在单词内断行。当这个属性被应用时,如果文本内容到达容器边界而无法在当前行继续显示时,浏览器将在单词内的任意字符处断开,以适应容器宽度,哪怕这意味着单词会被拆分。这在处理包含长英文单词或URL等连续无空格文本时特别有用,可以避免内容溢出容器。
  • white-space: pre-wrap;

    • white-space: pre-wrap;
      

      控制元素中的空白符和换行符如何处理。这个值结合了两方面特性:

      • pre: 保留空白符序列,比如空格和制表符,文本中的每一行都会按照源代码中的排版来显示,不会被折叠。
      • wrap: 允许文本自动换行。即使文本中包含换行符(\n),也会在必要时在这些换行符之外的地方换行,以避免内容溢出容器。

接下来需要处理css中的高度

// 设置css中的高度,需要传递一个参数,表示展开还是折叠
// true表示展开
const setCssVar = visible => {
  if (visible) {
    refMoreText.value.style.setProperty("--height", fullHeight + lineHeight + "px");
    refMoreText.value.style.setProperty("--row", "");
  } else {
    refMoreText.value.style.setProperty("--row", props.row);
    refMoreText.value.style.setProperty("--height", textHeight + "px");
  }
};
  • refMoreText.value: 在Vue中,ref是一个用于获取DOM元素或组件实例的引用。.value是在Vue的Composition API中访问ref引用的实际DOM元素或组件实例时所必需的。因此,refMoreText.value就是我们想要操作的DOM元素。
  • .style: 这是JavaScript DOM元素的一个属性,用于访问和修改元素的内联样式。
  • .setProperty: 这是style对象的一个方法,用于设置CSS样式,特别是对于CSS变量(也称为自定义属性)非常有用。它接受两个参数:属性名(在这里是CSS变量名)和属性值。
  • "--height" : 这是CSS变量的名称。CSS变量允许你在CSS中定义可重用的值,就像这里的--height,可以在多个地方引用并更新。
  • fullHeight + lineHeight + "px" : 这是设置给--height变量的新值。这里,fullHeightlineHeight应该是之前已经定义好的JavaScript变量,分别代表某个元素的全高和单行高度(单位可能是像素,但未明确指出)。这两个数值相加后,再加上字符串"px",以确保设置的高度值是以像素为单位的,这是CSS中常用的长度单位。

接下来判断是否显示标志

onMounted(() => {
  // 获取一开始的高度
  fullHeight = refContent.value.clientHeight;
  //  获取行高
  lineHeight = Number(window.getComputedStyle(refMoreText.value).lineHeight.slice(0, -2));
​
  // 折叠的高度
  textHeight = lineHeight * props.row;
​
  // 设置标识的行高
  if (refTrigger.value) {
    refTrigger.value.style["lineHeight"] = lineHeight + "px";
  }
  // 判断是都显示 标志
  isExceed.value = textHeight < fullHeight;
  // 设置css中的高度
  setCssVar();
});

添加切换事件

// 点击展开和隐藏
const handleTrigger = () => {
  // 修改是否显示表示
  moreVisible.value = !moreVisible.value;
  // 如果是展开就隐藏背景颜色,否则添加背景颜色
  if (moreVisible.value) {
    refTrigger.value.classList.remove("bg");
  } else {
    refTrigger.value.classList.add("bg");
  }
  // 重新设置css中高度变量
  setCssVar(moreVisible.value);
};

经过以上基本上就完成了

当尺寸变化之后,容器不能变化,需要修改一下

image-20240628180321006

image-20240628180259001

在发生变化之后需要获取一开始的高度、是否显示标识、如何设置css中的高度

let resizeObserver: ResizeObserver | null = null;
​
onMounted(() => {
  // 获取一开始的高度
  fullHeight = refContent.value.clientHeight;
  //  获取行高
  lineHeight = Number(window.getComputedStyle(refMoreText.value).lineHeight.slice(0, -2));
​
  // 折叠的高度
  textHeight = lineHeight * props.row;
​
  // 设置标识的行高
  if (refTrigger.value) {
    refTrigger.value.style["lineHeight"] = lineHeight + "px";
  }
  // 判断是都显示 标志
  // isExceed.value = textHeight < fullHeight;
  // 设置css中的高度
  // setCssVar();
  resizeObserver = new ResizeObserver(entries => {
​
    for (let entry of entries) {
      fullHeight = entry.contentRect.height;
      isExceed.value = textHeight < fullHeight;
      setCssVar(moreVisible.value);
    }
  });
  resizeObserver.observe(refContent.value);
});

ResizeObserver 是一个现代 JavaScript API,用于监听 DOM 元素的尺寸变化。在过去,监听元素尺寸变化通常依赖于 window 的 resize 事件,但这只能捕捉到窗口尺寸变化。ResizeObserver 为监控特定元素的尺寸变化提供了精确的方法,而不依赖于窗口尺寸。

回调函数的参数 entries 是一个包含 ResizeObserverEntry 对象的数组。每个ResizeObserverEntry 对象代表一个被观察的元素,并提供以下信息:

target: 被观察的元素。 contentRect: 一个 DOMRectReadOnly 对象,包含元素的尺寸和位置信息。可以使用 contentRect.width 和 contentRect.height 获取元素的新尺寸。 borderBoxSize: (可选)如果浏览器支持,这个属性会包含元素的边框尺寸。

经过这样处理就不会在出现上面的问题了

最后在组件销毁的时候应该停止监听

将代码中的text删除,打开props中的text这样文本就是传递的了,一个折叠展开组件就做好了