1、背景
前一阵子做需求,产品经理提了这样一个需求:当文本宽度过宽的时候,可以换行,如果换行超过三行,第三行最后面显示省略号。
当时我的内心:
2、实现
2.1 css实现
css中有一个-webkit-line-clamp属性,具体作用是:可以将块的内容限制在指定的行数内。
它接受一个整数,用来表明最大显示行数,取值范围:[1,正无穷]
要使此属性生效需要设置以下两个属性:
p {
display: -webkit-box 使容器成为一个弹性盒容器。
-webkit-box-orient: vertical 设置盒子的排列方向为纵向。
}
完整实现如下:
<p>
In this example the <code>-webkit-line-clamp</code> property is set to
<code>3</code>, which means the text is clamped after three lines. An ellipsis
will be shown at the point where the text is clamped.
</p>
p {
width: 300px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
但是目前这个属性浏览器支持度不好,不具有实际的使用价值。
2.2 js实现 - 滚动高度
原理:通过循环比较所有文本占据的高度scrollHeight和允许的行数所计算出来的最大高度maxHeight,如果scrollHeight 大于 maxHeight,则将删除文本尾部的一个字符。
核心逻辑如下:
function getLineHeight(ele) {
let lh = window
.getComputedStyle(ele, null)
.getPropertyValue("line-height");
if (lh == "normal") {
// Normal line heights vary from browser to browser. The spec recommends
// a value between 1.0 and 1.2 of the font size. Using 1.1 to split the diff.
lh =
parseInt(
window.getComputedStyle(ele, null).getPropertyValue("font-size")
) * 1.2;
}
return parseInt(lh);
}
function clampText(element, lines) {
const lineHeight = getLineHeight(element);
const maxHeight = lineHeight * (lines + 1);
let text = element.innerText;
let originalText = element.getAttribute("data-original-text") || text;
// 如果当前文本已经是截断的,尝试恢复原始文本
if (text.endsWith("...") && element.scrollHeight < maxHeight) {
element.innerText = originalText;
text = element.innerText;
}
// 截断文本
while (element.scrollHeight > maxHeight) {
text = text.substring(0, text.length - 1);
element.innerText = text + "...";
}
// 保存原始文本
if (!element.hasAttribute("data-original-text")) {
element.setAttribute("data-original-text", originalText);
}
}
核心的逻辑就是这一段:
// 截断文本
while (element.scrollHeight > maxHeight) {
text = text.substring(0, text.length - 1);
element.innerText = text + "...";
}
需要注意的是:由于获取
line-heigth的时候可能获取不到确切的数值,所以需要额外处理一下,但是不同浏览器也是有不同的差异的,
最终效果如下图:
如果有容器大小缩放的场景,还需要使用ResizeObserver来监听容器宽度的变化,来动态调整行数。
const ele = document.querySelector("p");
const observer = new ResizeObserver((entries) => {
for (let entry of entries) {
clampText(entry.target, 3);
}
});
observer.observe(ele);
完整实现如下:
3、使用clamp.js
Clamp.js是一个实现line-clamp的库,能够方便的设置折断的行数,同时也能够设置动画来拆分耗时计算(通过setTimeout实现)。其原理也是通过计算行高和容器高度实现的。
使用:
//一行
$clamp(myHeader, {clamp: 1});
//多行
$clamp(myHeader, {clamp: 3});
//基于容器高度自动计算
$clamp(myParagraph, {clamp: 'auto'});
//固定高度
$clamp(myParagraph, {clamp: '35px'});