1、功能
需要实现下面的功能,多行文本出现省略,并且提供展开和收起按钮。

2、技术方案与实现
1、用-webkit-line-clamp 实现
-webkit-line-clamp 指定显示几行,然后display、overflow等等设置一下就好了。
省略号的位置非常正确,但是问题是兼容性一般,想在省略号后面加按钮就有点困难。
css
.text {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
2、用float手动实现
- 提供一个省略号的dom,然后设置为右浮动;
- text-container容器设置一个伪元素挤压文字和省略号,之后再让文字content上去(margin-top为负值)
问题:当一个单词不够显示距离时会完全消失,导致省略号和文本有很长的一段距离,可以用文本的截断解决,但是英文状态会把单词给截断,这样也不好。这个问题可以用绝对定位来解决,但是可能出现省略号遮住文字的问题。。。
代码如下:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
.text-container {
width: 200px;
height: 100px;
overflow: hidden;
}
.text-container::before {
content: '';
display: block;
height: 80px;
}
.content {
margin-top: -80px;
line-height: 24px;
}
.more {
margin-right: 10px;
float: right;
line-height: 1;
}
</style>
<title>Document</title>
</head>
<body>
<div class="text-container">
<div class="more">...</div>
<div class="content">
瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁
</div>
</div>
</body>
</html>
效果如下:

3、react-组件
tsx:
javascript
import React, { memo, useEffect, useRef, useState } from 'react';
import CollapseText from './components/CollapseText';
import ExpendText from './components/ExpendText';
import styles from './index.scss';
/**
* 可展开文本组件的Props接口
*/
interface Props {
/** 要显示的文本内容 */
text: string;
/** 最大显示行数,超出后会截断显示省略号,默认为3行 */
maxLines?: number;
/** 自定义CSS类名 */
className?: string;
/** 收起按钮的文本,默认为"收起" */
collapseText?: string | React.ReactNode;
/** 展开按钮的文本,默认为"展开" */
expandText?: string | React.ReactNode;
/** 展开/收起回调 */
onToggleExpansion?: (isExpanded: boolean) => void;
}
/**
* 可展开文本组件
*
* 功能:
* - 根据指定行数限制文本显示
* - 超出行数时自动截断并显示展开按钮
* - 支持展开/收起功能
* - 支持自定义按钮文本
*
* @param props - 组件属性
* @returns React函数组件
*/
const ExpandableText: React.FC<Props> = ({
text,
maxLines = 3,
className = '',
collapseText = <CollapseText />,
expandText = <ExpendText />,
onToggleExpansion,
}) => {
// 控制文本是否展开的状态
const [isExpanded, setIsExpanded] = useState(false);
// 标记文本是否需要展开功能(即是否超出了指定行数)
const [needsExpansion, setNeedsExpansion] = useState(false);
// 文本容器的DOM引用,用于计算文本高度
const textRef = useRef<HTMLDivElement>(null);
const [peerContainerHeight, setPeerContainerHeight] = useState(0);
const [containerHeight, setContainerHeight] = useState(0);
/**
* 检测文本是否超出指定的行数
* 通过比较实际内容高度和最大允许高度来判断
*/
useEffect(() => {
if (textRef.current) {
// 获取当前元素的行高
const lineHeight = parseInt(getComputedStyle(textRef.current).lineHeight);
// 计算指定行数对应的最大高度
const maxHeight = lineHeight * maxLines;
setPeerContainerHeight(maxHeight);
setContainerHeight(maxHeight);
// 获取实际内容的滚动高度(包含被隐藏的部分)
const actualHeight = textRef.current.scrollHeight;
// 如果实际高度超过最大高度,则需要展开功能
setNeedsExpansion(actualHeight > maxHeight);
}
}, [text, maxLines]); // 当文本内容或最大行数改变时重新检测
/**
* 切换文本展开/收起状态
* @param e - 鼠标点击事件
*/
const toggleExpansion = (e: React.MouseEvent<HTMLButtonElement>) => {
// 阻止事件冒泡,避免触发父级元素的点击事件
e.stopPropagation();
const newIsExpanded = !isExpanded;
// 切换展开状态
setIsExpanded(newIsExpanded);
onToggleExpansion?.(newIsExpanded);
// 切换展开状态时,更新容器高度
if (newIsExpanded) {
setContainerHeight(textRef.current?.clientHeight || containerHeight);
} else {
setContainerHeight(peerContainerHeight);
}
};
return (
<div
className={`${styles['text-container']} ${className}`}
style={
{
'--container-height': `${containerHeight}px`,
'--line-height': `${24}px`,
} as React.CSSProperties
}
>
{needsExpansion && (
// <span className={isExpanded ? styles.more : styles['more-position-expend']}>
<span className={styles['more']}>
{!isExpanded && <span className={styles['more-text']}>... </span>}
<span className={styles['more-button']} onClick={toggleExpansion}>
{isExpanded ? collapseText : expandText}
</span>
</span>
)}
<div ref={textRef} className={`${styles['content']}`}>
{text}
</div>
</div>
);
};
export default memo(ExpandableText);
css:
javascript
.text-container {
height: var(--container-height);
overflow: hidden;
position: relative;
}
.text-container::before {
content: '';
display: block;
height: calc(var(--container-height) - var(--line-height, 24px));
}
.content {
margin-top: calc((var(--container-height) - var(--line-height, 24px)) * -1);
line-height: var(--line-height, 24px);
}
.more-position-expend {
padding-left: 10px;
position: absolute;
bottom: 4px;
right: 0;
background: white;
}
.more {
float: right;
line-height: var(--line-height, 24px);
}
.more-text {
font-size: 16px;
}
.more-button {
margin-left: 4px;
cursor: pointer;
color: rgb(255, 34, 102);
}
3、END
目前采用float的方式