微信朋友圈全文、收起功能实现

需求是这样的,一个列表页,每个条目最多只显示两行文字,超过两行显示省略号,并显示向下箭头,点击箭头展示全部内容。

刚接触这个需求的时候,也想了几种方案,如果用字符长度来判断是否显示箭头和省略号是最简单的,但是体验一定不好,首先手机屏幕的大小不确定,每个不同型号的手机一行显示多少个字符不确定,再加上有英文单词和数字的时候就更不能确定了。

css3里边有一个-webkit-line-clamp:2;这样的一个属性,当然也要搭配一些其他的属性。

那么就算是超过部分用省略号代替已经解决了,那么点击箭头展示全部怎么解决呢,动态改变css样式,那么一定需要一个状态值来判断,这个状态值就由js来做吧。这是初步想法。

看一下实现后的样子:
1.list列表

① 可以看到若是文字超过两行显示向下的箭头,省略号代替其他部分
② 若文字没有超过两行不显示箭头不显示省略号
③ 点击向下的箭头,显示该条的全部内容,箭头向上,点击向上箭头收起内容

那么现在我们看到是一个列表,布局就用ul li,因为列表里边既有一行的时候也有两行的时候,所以我给了li一个padding,然后里边放一个div,这个div里边包括文字和箭头。所以html是这样写的:

1
2
3
4
5
6
7
8
<ul>
<li :class="{'limit': item.state}" ref="li" v-for="(item, index) in zcList.data" @click.stop="zcNameC(item)" :key="index">
<div :class="['content', {'content-limit': item.state}]">
<div class="content-msg">{{item.content}}</div>
<span v-if="moreArr[index]" :class="item.state ? 'arrowdown': 'arrowtop'" @click.stop="deploy(item, index)"></span>
</div>
</li>
</ul>

对应的css样式是这样的:

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
li {
padding: 10px 15px 10px 0;
position:relative;
font-size: 1.5rem;
text-align: left;
line-height: 2.4rem;
}
.limit {
max-height: 70px;
overflow: hidden;
text-overflow:ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.content{
width: 100%;
position: relative;
}
.content-limit{
max-height: 50px;
overflow: hidden;
}
.content-msg{
padding-right: 44px;
}
.arrowdown{
background: url("../assets/down.png") no-repeat;
background-size: 10px 6px;
}
.arrowtop{
background: url("../assets/top.png") no-repeat;
background-size: 10px 6px;
}

可以看出来我们需要两个变量,一个moreArr数组来判断是否显示箭头,一个是每一个条目有一个箭头的状态是向上还是向下,项目太久,现在想完全可以使用一个状态来判断是否显示箭头。

js代码如下:

1
2
3
4
5
6
7
8
9
10
11
setTimeout(() => {
let lis = document.getElementsByClassName('content-msg')
for (let i = 0; i < lis.length; i++) {
if (lis[i].offsetHeight / 24 > 2) {
this.moreArr.push(true)
this.$set(this.zcList.data[i], 'state', true)
} else {
this.moreArr.push(false)
}
}
})

首先我们需要判断当前每个条目是否超过了两行,因为每行字有一行高,那么可以用包裹文字的div的高度除以每行字的行高,24px是每一行字的行高,若是大于2(依情况而定),那么需要给当前对象添加箭头,那么就在moreArr中添加一个true,并且将列表项添加state属性并为true

注意:一定要加setTimeout或者nextTick,先请求接口才能去获取包裹文字的div不然获取不到。因为我在请求接口的时候使用async await 将请求接口变成异步操作了,所以要把上边的方法变成异步的

那么点击箭头的时候就改变state的状态就好了,代码如下

1
2
3
4
5
deploy (item, index) {
if (item.hasOwnProperty('state')) {
item.state = !item.state
}
}

那么有了这个经验来写微信的全文查看以及收起全文就简单很多了,先看一下效果
2.朋友圈样式模仿

左边的图是没有点开全文的时候,右边是点开的时候。这个是类似于朋友圈的功能,上拉加载下一页,所以在请求接口后都要计算再次渲染的条目的高度。

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
<div :class="['con-outer',{'con-box': li.state} ]">
<p class="content-msg" v-html="$options.filters.toHtml(li.content)"></p>
</div>
<div><span v-show="moreArr[li.id]" class="btn-show" @click.stop="deploy(li,index)">{{li.state ? '全文':'收起'}}</span></div>

.con-outer{
position:relative;
font-size: 15px;
color: #666666;
line-height: 21px;
word-break:break-all;
text-align: justify;
padding-left: 15px;
}
.con-box{
max-height: 104px;
overflow: hidden;
text-overflow:ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5;
}

getStatus () {
setTimeout(() => {
let lis = document.getElementsByClassName('content-msg')
for (let i = 0; i < lis.length; i++) {
let item = this.list[i]
if (!item.state) {
if (lis[i].offsetHeight / 21 > 5.1) {
this.$set(item, 'state', true)
} else {
this.$set(item, 'state', false)
}
this.moreArr[item.id] = item.state
}
}
})
}

嗯哼,有分页,所以想每次添加state的状态的时候就只遍历新增加的条目,然而踩了好多坑。其实这个项目做了太久了快两年了,不记得当时踩了哪些坑然后这样写了, 最近有同事问起来,所以想着总结一下