多行文本溢出省略技术是一个难题,无论是在前端还是APP端。文字何时换行,换行的是哪一个字,文字会占用几行都难以精确计算和估量。本文将探讨和实现一种相对完美的方法。
单行文本省略
单行文本省略较为简单,且兼容性较好。
要实现单行文本省略,下面CSS属性缺一不可
width: 100px; // 固定宽度
overflow: hidden; // 溢出隐藏
white-space: nowrap; // 文字不换行
text-overflow: ellipsis; // 文字溢出截断
多行文本省略
当接到这个需求的时候,考虑到我们产品运行在微信小程序,而小程序在IOS和安卓平台都是用了webkit内核, 因而下面的代码应当在两个平台都可以很好运行。
webkit 内核的浏览器可以原生实现多行文本溢出省略。
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
然而事实并非如此。实际上面代码在安卓可以运行,但在IOS下则空白。
找了半天资料,也没有找到原因。猜想可能 WkWebView 中的 webkit 版本太低的缘故。
既然上面的代码不能使用,只能尝试其他办法。
JS 方式
JS计算裁剪的方式被排除了
- 第一,这有悖于行为与展示相分离的理念
- 第二,要考虑汉字,英文字母,图标,全角半角符号等才能相对准确算出文字占用几行,要不要裁切
- 第三,我们的使用场景中要解析富文本,对于HTML标签,链接地址等要考虑的较多,实现方案较为复杂
CSS 方式
方法一
很容易写下下面这些代码,缺点是固定高度,文字数量必须超过两行,当文字只有一行或两行时,都会显示末尾的省略号。且文字超过两行,末尾的省略号有可能遮挡住一半的字符。这样的效果不是我们想要的。
.container {
position: 'relative';
width: 200px;
height: 40px;
line-height: 20px;
overflow: hidden;
}
.container::after {
content: '...';
display: 'block';
position: 'absolute';
right: 0;
bottom: 0;
}
方法二
CSS 处理的难点在于知道有没有换行,思考良久后无果,从网上找了找资料,发现一种很巧妙的办法。解决了一部分问题。
该方法利用浮动元素特性实现。原理很简单,但想法很独特。下面给出一个限定两行的例子。
第一步:创建如图布局
<div class="container">
<div class="baseline"></div>
<div class="text">这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本</div>
<div class="omit">...</div>
</div>
.container {
background: #099;
max-height: 40px;
line-height: 20px;
.baseline {
float: left;
width: 20px;
height: 40px;
background: yellow;
}
.text {
float: right;
width: calc(100% - 20px);
background: blueviolet;
word-break: break-all;
}
.omit {
float: right;
content: "...";
width: 20px;
height: 20px;
background: red;
}
}
第二步:省略号归位
...
.omit {
float: right;
content: "...";
width: 20px;
height: 20px;
background: red;
+ position: relative;
+ left: 100%;
+ transform: translate(-100%, -100%);
}
...
这里利用 position: relative 相对父元素将自己定位到了父元素的宽度外,之后利用 transform 向上向左移动了一个自己的宽高。当只有一行时,省略号移出了视线之外,当两行时,省略号回归了正确的位置。
第三步:隐藏多余元素
...
.container {
background: #099;
max-height: 40px;
line-height: 20px;
++ overflow: hidden;
...
.text {
float: right;
-- width: calc(100% - 20px);
++ width: 100%;
++ margin-left: -20px;
background: blueviolet;
word-break: break-all;
}
...
这里使用 margin-left 为负,盖住了 baseline 元素。这样保证了基本布局不变,且达到了影藏无关元素的作用。由于在父元素中设定了 overflow: hidden, 清除浮动,所以父元素的背景颜色显示出来了。
局限性
注意
- container 元素中 maxHeight/Height 必须是 LineHeight 的整数倍。倍数表示限制几行
- container 元素中 maxHeight/Height 要与 baseline 元素的 height 保持一致
- baseline 元素宽度要与 emit 元素宽度保持一致
- emit 元素高度要为 container 元素的行高
应用案例
对比App Store中的评论,可以看出我们的实现还是相对完美