本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/39NCyZvm8EYiJ-pEEtjxGw 作者:何彦军
目前在移动端开发的展示界面中,如果一段文本的数量过长,受限于屏幕的宽高等因素,有可能不能完全显示,这个时候就会把溢出的文本显示成省略号。
最近就亲身经历了一系列类似的需求,于是这里做个总结和记录。
首先一个最基本的需求就是当文本超过一行最大宽度时,超出的部分变为省略号,如下图所示。
这个功能比较基础,只用css就可以实现,如下图所示代码块。
可是有时候产品同学希望显示的文本可以再多一点,于是就有了多行文本溢出显示省略号的需求,如下图所示。
这个功能也可以通过css实现,如下图代码块所示。
这里用到了webkit的css扩展属性,因此适用于webkit浏览器及移动端,并且在兼容性方面也有些影响,但是只要不是特别老旧的机器,还是完全能够支持的。
在支持了多行文本溢出显示省略号的功能之后,产品同学又发现了体验不友好的点,如下图所示。文本在第二行开头处就结束了,这就导致第二行大部分是空白的,影响了美观度。
因此,产品同学提出了一个新需求:
- 当文本没有超过第x行的一半时,则按第x-1行溢出显示省略号的方式展示;(第1行除外)
- 当文本超过第x行的一半但没有超过第x行时,则正常展示;
- 当文本超过第x行时,则按第x行溢出显示省略号的方式展示。
这就需要计算出文本实际占用的宽度才能选择采用哪种展示方式。
查找资料得知,canvas提供了一个measureText的方法,该方法的返回包含一个对象,这个对象里包含了以像素计的指定字体宽度。
如果需要在文本显示之前,就了解文本的宽度,那么可以使用该方法。
上面的代码显示效果如下:
于是可以基于canvas能力来实现这个功能,大概的流程图如下图所示。
这里最关键的是要计算出每一行可以显示多少文本,利用canvas的measureText方法,可以达到这个效果,伪代码如下所示。
虽然canvas可以计算出文本显示的宽度,并且兼容性和性能都还不错,但是当某一行末尾出现特殊符号或者是英文单词时,就会出现预期外的情况。
浏览器实际渲染出来的时候,如果末尾有特殊符号时会连同前面的字符一起换行,而如果末尾有英文单词时也会将这个英文单词换行展示。
但通过canvas计算出来的结果,会导致每一行的文本增多了,从而跟预期的展示效果有出入。
因此,这种方案只能适用于文本中不包含特殊符号和英文单词的场景。
一段时间后,产品同学感觉展示那么一段文本有点儿单调,于是又提出了一个进阶版的需求:
- 文本的首行开头需要缩进或者可以配置一个图标;
- 文本的末尾可以配置按钮或者图标,并且如果文本超过了范围需要显示省略号,但是省略号需要在按钮或图标的前面。
顺带给出了一个参考示例,如下图所示。
由于之前没有遇到过类似的需求,于是开始google这个问题,经过海量的检索,终于发现了该问题实现的方案。
原来网上也有很多朋友遇到了这样的需求,并专门写了单独的组件来使用,比如:
HeyUI:https://www.heyui.top/component/other/textellipsis
vue-text-ellipsis:https://github.com/Luobata/vue-text-ellipsis
它们的思路都是通过最终展示的实际高度是否超过预期的容器高度来判断是否需要删减文本。其流程图大概如下图所示。
就这样,通过现成的组件就解决了一个难题。
然鹅,本以为需求已经告一段落了,哪知产品同学又开始了体验上的深度挖掘。
她们认为,展示的文本样式不应该都是一样的。
有些文本表达的意思可能比较重要,这就需要重点引起用户的注意。
而有些文本表达的意思可能重要程度一般,这就不需要用户注意。
于是乎她们又提出了一个通过高亮文本来提升用户体验的需求:
- 能够根据文本的标记进行高亮展示
比方说,获取到下面一段文本,它要显示出入下图所示的那样高亮效果。
由于文本高亮需要通过标签将文本包裹起来并添加高亮样式才能实现,而之前的组件是通过v-text的方式实现的,因此这里不能直接使用,需要将组件改造成v-html的方式插入才可以。
假如通过v-html插入文本,并且设置了em标签的样式,那么就会有一个问题,组件是通过循环剔除最后一个字符直到实际高度小于容器高度来实现展示功能的,这就有可能截掉标签字符,导致最后的展示有异常。
所以,在截取文本的时候还需要做一些处理,流程图如下图所示。
到这里,已经实现文本的一种高亮形式了,但是假如有好几个部分的文本需要高亮且高亮的样式还各不相同,这又要怎么解决呢?
一种思路是,通过几种不同名称的标签分别包裹需要高亮的文本,每一种标签会对应一种高亮样式,这样的话,在获得源文本后,首先通过词法分析将源文本中的标签解析出来,后面的流程就跟上图步骤1后面的流程类似了。