小程序怎么實現“全文收起”功能?下面本篇文章小程序實現多行文本“全文收起”功能的方法,希望對大家有所幫助!

小程序里經常會碰到需要實現多行文本”全文收起“功能,在掘金上有搜索到用純css實現。親測:ios很完美,andriod上的無效。
小程序社區有很多方案,目前在社區有看到一位大佬使用js動態計算告訴去實現,親測大致有效果,測試后,在一些特殊情況下,計算會有誤差,所以有更改些許代碼。
一、需求
位于多行文本右下角,展示”全文/收起“按鈕
“展開”和“收起”兩種狀態的切換
當文本不超過指定行數時,不顯示”全文/收起“按鈕
文本顯示【全文】展示狀態下,更新數據,文本不被收起
二、實現思路
1、多行文本截斷
主要用到用到 line-clamp,關鍵樣式如下
.text-clamp3 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}2、判斷文本是否超出指定行數,顯示全文 收起 按鈕
編寫兩段文本,一段展示完整的文本A,一段展示使用 line-clamp省略后的文本B,因為B有被截取,因此B的高度相對較小。對比兩段文本的高度,便可以知道文本是否超出兩行
在小程序里,可以使用wx.createSelectorQuery()獲取文本高度
js
const query = wx.createSelectorQuery().in(this);
query.selectAll(".showArea, .hideArea").boundingClientRect(res => {
console.log(res, 'res')
}).exec()
三、代碼實現
1、初次版本
根據設計思路,立馬上手代碼
foldable.wxml
<view class="content">
<view class="contentInner content-inner-class showArea {{!onFold ? 'text-clamp' + maxLine : ''}}">{{content}}</view>
<view class="contentInner content-inner-class hideArea" style="width: {{width}}px">{{content}}</view>
<view class="foldInner fold-class {{position === 'right' ? 'flex-end' : 'flex'}}" wx:if="{{showFold}}">
<text class="fold" catchtap="handleFold">{{onFold ? unFoldText : onFoldText}}</text>
</view>
</view>foldable.js
/**
* 長文本內容展開與收起
* @param {String} content 長文本內容
* @param {Number} maxLine 最多展示行數[只允許 1-5 的正整數]
* @param {String} position 展開收起按鈕位置[可選值為 left right]
* @param {Boolean} foldable 點擊長文本是否展開收起
* @param { String } onFoldText 收縮時文字
* @param { String } unFoldText 展開時文字
*
*/
Component({
externalClasses: ['content-inner-class', 'fold-class'],
properties: {
content: {
type: String,
observer(val) {
if (this.data.onReady) {
this.getNodeClientReact()
}
}
},
maxLine: {
type: Number,
value: 1,
observer(value) {
if (!(/^[1-5]$/).test(value)) {
throw new Error(`maxLine field value can only be digits (1-5), Error value: ${value}`)
} else if (this.data.onReady) {
this.getNodeClientReact()
}
}
},
position: {
type: String,
value: "left"
},
foldable: {
type: Boolean,
value: true
},
// 收縮時文字
onFoldText: {
type: String,
value: "全文"
},
// 展開時文字
unFoldText: {
type: String,
value: "收起"
},
},
data: {
width: null,
onFold: false,
showFold: false,
onReady: false
},
lifetimes: {
attached() {
this.getNodeClientReact()
this.setData({
onReady: true
})
},
},
methods: {
getNodeClientReact() {
setTimeout(() => this.checkFold(), 10)
},
checkFold() {
const query = this.createSelectorQuery();
query.selectAll(".showArea, .hideArea").boundingClientRect(res => {
let showFold = res[0].height < res[1].height;
this.setData({
width: res[0].width,
showFold,
})
}).exec()
},
handleFold() {
this.setData({
onFold: !this.data.onFold
})
}
}
})foldable.wxss
.content {
width: 100%;
position: relative;
overflow: hidden;
}
.contentInner {
word-break: break-all;
width: 100%;
color: #2f3033;
font-size: 30rpx;
line-height: 1.35;
}
.hideArea {
display: -webkit-box;
overflow: hidden;
position: fixed;
top: 100vh;
left: -100vw;
}
.foldInner {
padding-top: 10rpx;
color: #6676bd;
font-size: 32rpx;
}
.foldInner .fold {
cursor: pointer;
}
.text-clamp1 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
.text-clamp2 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.text-clamp3 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
.text-clamp4 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 4;
}
.text-clamp5 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5;
}2、修復版本
正常情況下,此方法可行,但是在級別文字下,會計算錯誤。經過測試,可將 節點是.hideArea的內容定位在.showArea節點下可解決
foldable.wxss
.hideArea {
display: -webkit-box;
overflow: hidden;
/* position: fixed;
top: 100vh;
left: -100vw; */
position: absolute;
top: 0;
left: 0;
z-index: -1;
color: #fff;
}3、增強版本
經過修復之后,本來是可以完美實現了,但是在測試過程中,第一次正常渲染是沒有問題。但如果文本數據更新,會發現如果原來的文本從一行增加到兩行時,使用wx.createSelectorQuery()計算的高度會有存在是實際高低的兩倍的現象。導致會錯誤出現【全文】文字。然后文本從兩行增加到三行或者多行都沒問題,不太理解為什么會出現這個錯誤計算的現象。(期待大神能留言告知 ? )

為了彌補這個坑,我引入了lineHieght這個屬性。
// foldable.js
Component({
properties: {
lineHieght: {
type: Number,
observer(value) {
if (!(/^[0-9]*$/).test(value)) {
throw new Error(`lineHieght field value can only be digits`)
}
}
}
}
})通過lineHieght和最多可展示行數maxLine可以計算出,可在界面展示的最大高度。
// 文本可見的最大高度 const maxHeight = this.data.lineHieght * this.data.maxLine;
當然了,我們也需要適配不同的設備,而且通過wx.createSelectorQuery()計算出來的結果是以px為單位的。
所以,行高需要根據設備尺寸去改變。因為我們是以寬度是750px尺寸為設計稿的,所以根據wx.getSystemInfoSync()可以獲取設備信息,進而轉換成px的尺寸。
// foldable.js
changeRpxToPx(rpxInteger) {
return wx.getSystemInfoSync().windowWidth / 750 * rpxInteger
},因此,更新checkFold方法
checkFold() {
const query = this.createSelectorQuery();
query.selectAll(".showArea, .hideArea").boundingClientRect(res => {
let showFold = res[0].height < res[1].height;
const lineHeightToPx = this.changeRpxToPx(this.data.LineHeight);
// 展示區域高度(即是可能會被截取的可見文字)
const showAreaHeight = res[0].height;
// 隱藏區域的高度(即是完整文本高度,偶然事件會計算錯誤)
const hideAreaHeight = res[1].height;
// 文本可見的最大高度
const maxHeight = lineHeightToPx * this.data.maxLine;
// 如果是一行文字,偶然計算錯誤,用行高判斷
if (this.data.LineHeight && showAreaHeight <= maxHeight) {
showFold = hideAreaHeight > maxHeight
}
this.setData({
width: res[0].width,
showFold,
})
}).exec()
},4、最終版本
經過上一個版本,基本功能都已經實現。但是,如果文本超過最大行數,并且在展開全文的情況下,更新了文本,此時,全文/展開按鈕會展示錯誤。


通過分析代碼可知,在展開全文的狀態下更新了文本,此時.showArea節點和.hideArea節點的高度一致,執行代碼let showFold = res[0].height < res[1].height;,會返回false,因此按鈕會消失。
因此解決方案為:
// 如果文本超出最大行數,并且是顯示全文的狀態下,再次更新了文字
let onFold = false
if (showAreaHeight == hideAreaHeight && showAreaHeight > maxHeight) {
showFold = true
onFold = true
}所以最終版本的checkFold方法是:
checkFold() {
const query = this.createSelectorQuery();
query.selectAll(".showArea, .hideArea").boundingClientRect(res => {
let showFold = res[0].height < res[1].height;
const lineHeightToPx = this.changeRpxToPx(this.data.LineHeight);
// 展示區域高度(即是可能會被截取的可見文字)
const showAreaHeight = res[0].height;
// 隱藏區域的高度(即是完整文本高度,偶然事件會計算錯誤)
const hideAreaHeight = res[1].height;
// 文本可見的最大高度
const maxHeight = lineHeightToPx * this.data.maxLine;
// 如果是一行文字,偶然計算錯誤,用行高判斷
if (this.data.LineHeight && showAreaHeight <= maxHeight) {
showFold = hideAreaHeight > maxHeight
}
// 如果文本超出最大行數,并且是顯示全文的狀態下,再次更新了文字
let onFold = false
if (showAreaHeight == hideAreaHeight && showAreaHeight > maxHeight) {
showFold = true
onFold = true
}
this.setData({
width: res[0].width,
showFold,
onFold,
})
}).exec()
},四、代碼片段
經過多次測試,修改,最后附上代碼片段:
https://developers.weixin.qq.com/s/GWj19vmC7oxp
各位大神如果有更好的建議,可留言哦~~~






