多行文本缩略的实现

目前大多数浏览器不支持多行文本缩略,因此需要做兼容处理。比较好的思路是在首次渲染后去读取每个文字的宽度,计算是否应该展示缩略效果。为了能够读取每个文字的宽度,需要使用 span 包裹每个文字。

  1. 在非缩略状态下,通过读取每个文字的宽度,计算是否应该缩略
  2. 如果需要缩略,则使用一个 span 包裹前几行不需要缩略的文字,用另一个 span 包裹后几行需要缩略的文字
  3. 如果不需要缩略,使用 span 包裹每一个文本
  4. 如果文本更新了
    4.1 如果当前处于缩略状态,则先重置为非缩略状态,然后在跳转到步骤 1
    4.2 如果当前处于非缩略状态,则直接跳转到步骤 1

React 的示例代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import React, { useState, useRef, useEffect } from 'react';

export interface AutoEllipsisProps {
text?: string;
maxLine?: number;
className?: string;
onEllipsis?: (isEllipsis: boolean) => void;
}

export default function AutoEllipsis(props: AutoEllipsisProps) {
const { text, maxLine = 1, className, onEllipsis } = props;

const [isEllipsis, setIsEllipsis] = useState(false);
const [ellipsisLineFirstTextIndex, setEllipsisLineFirstTextIndex] =
useState(0);
const [rerenderAfterReset, setRenderAfterReset] = useState(0);

const divRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (isEllipsis) {
setIsEllipsis(false);
setRenderAfterReset((pre) => pre + 1);
return;
}

if (divRef.current && text) {
const containerWidth = divRef.current.getBoundingClientRect().width;

const headIndexList: number[] = [0];
let cumulativeWidth = 0;
const children = divRef.current.children as never as HTMLSpanElement[];

for (let i = 0; i < children.length; i++) {
const childWidth = children[i].getBoundingClientRect().width;
if (cumulativeWidth + childWidth <= containerWidth) {
cumulativeWidth += childWidth;
} else if (cumulativeWidth + childWidth > containerWidth) {
cumulativeWidth = childWidth;
headIndexList.push(i);
}
}

if (headIndexList.length > maxLine) {
setIsEllipsis(true);
setEllipsisLineFirstTextIndex(headIndexList[maxLine - 1]);
}
}
}, [text, rerenderAfterReset]);

useEffect(() => {
onEllipsis?.(isEllipsis);
}, [isEllipsis]);

const renderText = () => {
if (!text) {
return null;
}

if (!isEllipsis) {
return text
?.split('')
.map((item, index) => <span key={index}>{item}</span>);
}

return (
<>
<span>{text.slice(0, ellipsisLineFirstTextIndex)}</span>
<span
style={{
display: 'inline-block',
width: '100%',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
>
{text.slice(ellipsisLineFirstTextIndex)}
</span>
</>
);
};

return (
<div className={className} ref={divRef}>
{renderText()}
</div>
);
}


感谢您的阅读,如果发现文章中有错误或漏洞,请批评指正。
邮箱:aadonkeyz@gmail.com

0%