Small programs often encounter the need to achieve multi-line text “full text” function, in the nuggets have to search to use pure CSS. Pro test: Ios is perfect, android doesn’t work.

Small program community has a lot of programs, at present in the community have seen a big guy using JS dynamic calculation to tell to achieve, pro test roughly have the effect, after testing, in some special cases, calculation will have errors, so there are some changes in the code.

A, requirements,

  • In the lower right corner of the multi-line text, display the “full/Fold” button
  • Switch between “expand” and “fold” states
  • When the text does not exceed the specified number of lines, the “full text/fold” button is not displayed
  • 【 full text 】 In the display state, the data is updated, the text is not closed

Second, implementation ideas

1. Multi-line text truncation

Line-clamp is mainly used, and the key styles are as follows

.text-clamp3 {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}
Copy the code

2, determine whether the text exceeds the specified number of lines, displayThe full text Pack upbutton

Write two texts, one showing the complete text A and the other showing the text B omitted by line-clamp. Since B was intercepted, the height of B was relatively small. Compare the height of two paragraphs of text to see if the text goes beyond two lines

In the applet, you can use wx.createsElectorQuery () to get the height of the text

js

const query = wx.createSelectorQuery().in(this);
query.selectAll(".showArea, .hideArea").boundingClientRect(res => {
console.log(res, 'res')
}).exec()
Copy the code

Three, code implementation

1. Initial version

Follow the design idea and start coding immediately

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>Copy the code

foldable.js

@param {Number} maxLine Specifies the maximum Number of lines to display (positive integers 1-5 only) * @param {String} @param {Boolean} foldable long text or not * @param {String} onFoldText text or not * @param {String} unFoldText Expanded text * */ 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: r}, foldable: {type: String, value: r}, foldable: {type: String, value: r}, foldable: {type: String, value: r} UnFoldText: {type: String, value: "fold"},}, 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 }) } } })Copy the code

foldable.wxss

.content { width: 100%; position: relative; overflow: hidden; } .contentInner { word-break: break-all; width: 100%; color: #2f3033; font-size: 30rpx; The 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; }Copy the code

2. Fixed version

Under normal circumstances, this method works, but under level literals, the calculation is wrong. After the test, the contents of the. HideArea node can be located under the. ShowArea node

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;
}
Copy the code

3. Enhanced version

After the fix, it should have been perfectly implemented, but during the testing process, the first normal rendering was fine. However, if the text data is updated, you will find that the height calculated using wx.createsElectorQuery () is twice the actual height if the original text is increased from one line to two lines. Lead to errors in [full text] text. Then the text increases from two lines to three or more lines without any problem. I don’t understand why this miscalculation occurs. (Look forward to your message at 😉)

To remedy this, I introduced the property 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`) } } } } })Copy the code

Calculate the maximum height that can be displayed on the screen by using lineHieght and maxLine.

// maxHeight = this.data. LineHieght * this.data. MaxLine; // maxHeight = this.data.Copy the code

Of course, we also need to adapt to different devices, and the results calculated by wx.createsElectorQuery () are in px units.

Therefore, the line height needs to be changed according to the device size. Since we are designing with a width of 750px, wx.getSystemInfoSync() will get the device information and convert it to px.

// foldable.js
changeRpxToPx(rpxInteger) {
  return wx.getSystemInfoSync().windowWidth / 750 * rpxInteger
},
Copy the code

Therefore, update the checkFold method

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; // Height of hidden area const hideAreaHeight = res[1]. Height; // The maximum height visible in the text const maxHeight = lineHeightToPx * this.data.maxline; // If it is a line of text, and there is an accidental miscalculation, If (this.data.LineHeight && showAreaHeight <= maxHeight) {showFold = hideAreaHeight > maxHeight} this.setData({showFold = hideAreaHeight > maxHeight} width: res[0].width, showFold, }) }).exec() },Copy the code

4. Final version

After the last version, the basic functions have been realized. However, if the text exceeds the maximum number of lines, and the text is updated in the case of the full text expansion, the full/expand button displays an error.

Let showFold = res[0]. Height < res[1]. Height; let showFold = res[0]. , returns false, so the button disappears.

Therefore, the solution is:

// If the text exceeds the maximum number of lines and is displayed in the full text state, Let onFold = false if (showAreaHeight == hideAreaHeight && showAreaHeight > maxHeight) {showFold = true onFold = true }Copy the code

So the final version of the checkFold method is:

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; // Height of hidden area const hideAreaHeight = res[1]. Height; // The maximum height visible in the text const maxHeight = lineHeightToPx * this.data.maxline; // If it is a line of text, and there is an accidental miscalculation, If (this.data.lineheight && showAreaHeight <= maxHeight) {showFold = hideAreaHeight > maxHeight} // If (this.data.lineheight && showAreaHeight <= maxHeight) And is the full text of the state, Let onFold = false if (showAreaHeight == hideAreaHeight && showAreaHeight > maxHeight) {showFold = true onFold = true } this.setData({ width: res[0].width, showFold, onFold, }) }).exec() },Copy the code

Code snippets

After many tests, modifications, and finally attached code snippets. If you have better suggestions, please leave a message