先看下效果
组件库查看 组件地址
创建一个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变量的新值。这里,fullHeight和lineHeight应该是之前已经定义好的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);
};
经过以上基本上就完成了
当尺寸变化之后,容器不能变化,需要修改一下
在发生变化之后需要获取一开始的高度、是否显示标识、如何设置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这样文本就是传递的了,一个折叠展开组件就做好了