React如何在不打断英文单词情况下截断长文本

文本过长用省略号来进行截断css就可以实现,不过默认的效果无法实现单词不被截断,所以就用js实现了一版, 这样不仅可以做到单词不被截断,也可以做到在css不能使用的时候进行截断,比如在canvas中需要截断文字的情况

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
import { useCallback } from 'react';

const chineseCharacterPattern = new RegExp('[\u4E00-\u9FA5]');

const getStringWidth = (function () {
const contextMap = new Map<number, CanvasRenderingContext2D>();
return (str: string, fontSize: number) => {
let context = contextMap.get(fontSize) ?? null;
if (!context) {
const span = document.querySelector('span');
if (span) {
const canvas = document.createElement('canvas');
context = canvas.getContext('2d');
if (context && span) {
const fontFamily = window
.getComputedStyle(span, null)
.getPropertyValue('font-family');
context.font = `${fontSize}px ${fontFamily}`;
contextMap.set(fontSize, context);
}
}
}
if (context) {
const metrics = context.measureText(str);
return metrics.width;
}
return str.length * fontSize;
};
})();

function splitStr(str: string, separator: RegExp) {
const result = [];
let startIndex = 0;
for (let i = 0; i < str.length; i++) {
const current = str[i];
if (chineseCharacterPattern.test(current) || separator.test(current)) {
if (startIndex < i) {
result.push(str.slice(startIndex, i));
}
result.push(current);
startIndex = i + 1;
}
}
if (startIndex < str.length) {
result.push(str.slice(startIndex));
}
return result;
}

export function useTruncateLongString(
maxPixelLength: number,
fontSize: number,
omission = '...',
separator = /,? +/,
) {
const truncateLongString = useCallback(
(str: string): [string, boolean] => {
if (getStringWidth(str, fontSize) <= maxPixelLength) {
return [str, false];
}
const strSplitBySeparator = splitStr(str, separator);
const firstWord = strSplitBySeparator[0];
if (getStringWidth(firstWord, fontSize) > maxPixelLength) {
return [`${firstWord}${omission}`, true];
}
let currentWidth = getStringWidth(omission, fontSize);
const result = [];

for (const word of strSplitBySeparator) {
const wordWidth = getStringWidth(word, fontSize);
currentWidth += wordWidth;
if (currentWidth < maxPixelLength) {
result.push(word);
} else {
return [`${result.join('')}${omission}`, true];
}
}

return [result.join(''), false];
},
[maxPixelLength, fontSize, omission, separator],
);

return truncateLongString;
}