vue动态换行,行末省略
# vue 动态换行,行末省略
vue sfc 场景描述
- 一行可展示多个记录
- 单个记录超过一行,换行展示,文末省略
- 单个记录无法占满一行,可与多个无法占满一行的记录共一行
- 记录展示顺序按返回的数组内容一致,无需先根据内容长度排序
需求效果:
# css 单行省略无法实现原因
我们一般在看到单行省略,脑中都会想到用 css
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
1
2
3
2
3
无法实现原因:
- 元素必须是 块状元素,首先便不满足一行多个记录的情况,直接排除
# 实现方案一
- 手动计算截取(需要优先知道一行最多能放几个字符)
- 针对非中文字符(预估以两个非中文字符占一个中文字符的宽度,不准)
html
<span class="tags" auId="au__tag">
{{ formatName(item.value) }}
</span>
1
2
3
2
3
js
methods: {
formatName(name) {
if (!name) return ''
// 特殊字符
const _reg = /[0-9a-zA-Z,./\]{}()?|~`!@#$^&*<>+-=_ ]/g
let validCount = 0
let i = 0
// 以一行最多占23个字符来算
while (validCount < 23 && i < name.length) {
if (_reg.test(name[i])) validCount += 0.5 // 如果是非中文字符,只占0.5个字符
else validCount += 1
i++
}
// 行末截断,省略
return validCount >= 23 ? name.slice(0, i) + '...' : name
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
css
.tags {
display: inline-block; /* 设置为行内块状元素 */
margin: 24rpx 24rpx 0 0;
font-size: 26rpx;
line-height: 36rpx;
padding: 10rpx 24rpx;
background: #f8f8f8;
border-radius: 27rpx;
user-select: none;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
缺点:
- 需要预先知道一行最多能占几个字符(最好实践是在用 rem 的移动端)
- 预估非中文字符占的宽度,不准确
缺点效果:
优点:
- 可以给当前元素添加额外的内容,比如:右上角的删除按钮(该元素没有添加
overflow: hidden
)
优点效果:
# 实现方案二
- 通过 vue 自定义指令实现 (opens new window)
- 在指令 inserted 钩子中计算当前 body 宽度,以及当前 记录 占的宽度
- 如果超出了 body 的宽度,就给当前元素添加 单行省略的代码
html
<span class="tags" auId="au__tag" v-ellipsis="60">
<!-- 60: 是指当前内容占满一行时,加上60刚好是body的宽度,需要手动传入 -->
{{ formatName(item.value) }}
</span>
1
2
3
4
2
3
4
js
export default {
directives: {
ellipsis: {
inserted(el, binding, vnode) {
let width = vnode.context.$root.domWidth; // 去缓存的 body 的宽度
if (!width) {
// 通过根组件缓存当前body宽度,不用多次计算
vnode.context.$root.domWidth = width = document.body.clientWidth;
}
const textWidth = el.clientWidth; // 当前元素的宽度
const W = binding.value || 0; // 针对添加了 padding的需要手动传入大小,
if (width <= textWidth + W) {
// 如果当前元素的宽度 + padding * 2 大于等于 body 的宽度
// 说明当前元素已经超出一行了,此时添加单行省略
el.style.display = "block";
el.style.textOverflow = "ellipsis";
el.style.overflow = "hidden";
}
},
update(el, binding, vnode) {
const width = vnode.context.$root.domWidth;
if (!width) {
// 如果 update 执行了,说明 inserted 已经执行过了,width会存在
return;
}
const textWidth = el.clientWidth;
const W = binding.value || 0;
if (width <= textWidth + W) {
el.style.display = "block";
el.style.textOverflow = "ellipsis";
el.style.overflow = "hidden";
el.style.whiteSpace = "nowrap";
}
},
},
},
};
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
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
css
.tags {
display: inline-block; /* 设置为行内块状元素 */
margin: 24rpx 24rpx 0 0;
font-size: 26rpx;
line-height: 36rpx;
padding: 10rpx 24rpx;
background: #f8f8f8;
border-radius: 27rpx;
user-select: none;
white-space: nowrap; /* 设置文本不换行 */
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
效果:
缺点:
- 需要一个参照物,上面例子是以 body 为参照物,来计算一行的最大宽度
- 因为添加了
overflow: hidden
,所以如果要给 当前元素添加 右上角的关闭按钮将无法实现
eg:
优点:
- 可以自定义参照物,比如当前元素的父节点。
自定义参照物
export default {
directives: {
ellipsis: {
inserted(el, binding, vnode) {
let width = vnode.context.$root.domWidth;
if (!width) {
vnode.context.$root.domWidth = width =
vnode.context.$parent.$el.clientWidth; // 以当前元素的直接父级作为参照物,计算一行宽度
}
const textWidth = el.clientWidth;
const W = binding.value || 0;
if (width <= textWidth + W) {
el.style.display = "block";
el.style.textOverflow = "ellipsis";
el.style.overflow = "hidden";
}
},
},
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
上次更新: 2022/01/06, 19:59:43