最近碰到一个业务需求,描述文字超过3行的话,要在最后显示展开按钮,点击展开后显示所有文本,咋一看挺简单的,但是分析了一下其实还是有挺多东西需要考虑到的
效果大概这样
收起
展开
最开始想偷个懒,用css来做,使用-webkit-line-clamp属性来实现。
-webkit-line-clamp用来限制在一个块元素显示的文本的行数,结合text-overflow可以非常简单的用css实现收起的效果
overflow : hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
效果是这样的
然后展开就把-webkit-line-clamp属性去掉就可以了,但是问题来了,需求要求展开的按钮一定要和文字在同一行,用这个属性没有办法控制最后一行的长度,文字把按钮的位置占用了,所以没办法,只能老老实实用js来写了
大概有这么几个点
- 判断文字的长度,是否需要展开
- 根据3行文本的长度,截取收起时的文字
- 判断展开后的文字,是否会占用到按钮的位置,如果占用的话,按钮就要显示在下一行
接下来就是实现了,项目用的是vue,不过整体的思路是差不多的,无非是语法有一些变化
<template>
<div class="m_text">
<div ref="temp" class="temp">
{{ text }}
</div>
<div v-if="needExpend">
<div v-if="!expend" class="text">
{{ shortText }}
<span class="expend_btn" @click="showMore">更多</span>
</div>
<div v-else class="expend-text">
{{ text }}
<p v-if="btnInNextLine" class="expend_btn_next_line" @click="showLess">
收起
</p>
<span v-else class="expend_btn" @click="showLess">收起</span>
</div>
</div>
<div v-else>
{{ text }}
</div>
</div>
</template>
<script>
/* eslint-disable */
export default {
props: {
// 文本内容
text: {
type: String,
default: '',
},
},
data() {
return {
// 是否需要展开
needExpend: true,
// 切换展开收起
expend: false,
// 收起时的文本
shortText: '',
// 按钮的位置是否和展开后的文本冲突
btnInNextLine: false,
}
},
mounted() {
this.init()
},
watch: {
text() {
this.init()
},
},
methods: {
// 展开
showMore() {
this.expend = true
},
// 收起
showLess() {
this.expend = false
},
init() {
// 一行的宽度
const width = parseInt(window.getComputedStyle(this.$refs.temp).width)
// 字体的大小
const fontSize = parseInt(
window.getComputedStyle(this.$refs.temp).fontSize
)
// 收起后文本的长度
const shortText = Math.floor(width / fontSize) * 3 - 4 // 减去省略号及更多按钮的位置
// 是否需要展开
if (this.text.length < shortText) {
return (this.needExpend = false)
}
this.needExpend=true
// 最后一行文字是否和按钮冲突
if (
this.text.length % Math.floor(width / fontSize) >
Math.floor(width / fontSize) - 4
) {
this.btnInNextLine = true
}
// 截取收起后的文本
const arr = this.text.split('').slice(0, shortText)
// 添加省略号
arr.push('...')
// 得到收起后的文本
this.shortText = arr.join('')
},
},
}
</script>
<style lang="less" scoped>
.m_text {
font-size: 18px;
padding: 0 12px;
position: relative;
.temp {
// width: 100%;
visibility: hidden;
position: absolute;
left: 12px;
right: 12px;
}
.text {
position: relative;
}
.expend-text {
position: relative;
}
.expend_btn {
font-size: 18px;
color: #f39c11;
position: absolute;
bottom: 0;
right: 10px;
}
.expend_btn_next_line {
font-size: 18px;
color: #f39c11;
margin-right: 10px;
float: right;
}
}
</style>
整体的思路大概就是这个样子,当然还有很多可以优化的地方,比如几行后收起可以作为参数传进来,展开和收起按钮也可以传进来,不过整体的思路基本就是这个样子