![42cb6e610edd508bfb80ed0d6c3fbbc1.png](https://i-blog.csdnimg.cn/blog_migrate/25e891c3928c5cdaee775035a7b55c95.jpeg)
先看效果
![1c9d35fc2866ca9dcc6eadc4568af82a.png](https://i-blog.csdnimg.cn/blog_migrate/90def001cc90a6b65fdb4531363caf9d.jpeg)
![810f26abef84c123c6176fc6b72449f6.png](https://i-blog.csdnimg.cn/blog_migrate/b6eabfcf5d5cf6bb1b8c76379a4a0af4.jpeg)
主要结构
我用的mpvue,如用原生标签直接转换成原生即可
<div id="labelBox">
<div class="label userLabel" v-for="(label,inx) in labelList" :key="inx">{{label}}</div>
<div class="more" v-show="showLabel===1" id="moreLabel" @click="openMore">
<div>全部{{allLabel.length}}个</div>
<img class="icon ml5" src="/static/img/i_label_down.png" />
</div>
<div class="more" v-show="showLabel===2" @click="closeMore">
<div>收起</div>
<img class="icon ml5" src="/static/img/i_label_up.png" />
</div>
</div>
export default {
data() {
return {
labelList: [], // 视图显示的标签集合
allLabel: [], // 所有的标签集合
firstLabel: [], // 默认显示的标签集合
showLabel: 1, // 0 两个按钮都不显示,1 显示展开,2 显示收起
}
},
...
}
思路
利用小程序api NodesRef.boundingClientRect 获取节点的位置与大小信息,主要用到 width
,left
,right
- 循环所有标签(
.userLabel
),看是否有多行,通过所有节点的left
去判断,如果left
相同的有多个,就证明有多行 - 获取标签父级(
#labelBox
)的宽度width
- 获取到按钮(
#moreLabel
)的宽度 - 过滤第一行节点的
right
,如果与按钮的width
相加小于等于父级盒子的width
就保留
具体的代码
wxp为微信接口Promise化,会在之后列出用到的
export default {
data() {
return {
labelList: [], // 视图显示的标签集合
allLabel: [], // 所有的标签集合
firstLabel: [], // 默认显示的标签集合
showLabel: 1, // 0 两个按钮都不显示,1 显示展开,2 显示收起
}
},
methods: {
async loadPageData(){
// 请求后台数据
const res = ...
// 设置
this.allLabel = res.labes; // 记录所有的标签
this.labelList = this.allLabel; // 先插入所有表情
// 设置状态
if(this.allLabel.length>0){
await wxp.timeout(300); // 插入视图之后不会马上获取到节点信息,延迟获取
this.setLabelStauts();
}
},
// 设置标签状态
async setLabelStauts(){
const boxDom = await wxp.getElementById('#labelBox');
const labelDoms = await wxp.getElementsByClassName('.userLabel');
const btnDom = await wxp.getElementById('#moreLabel');
const left = labelDoms[0].left;
// 分行转为二维数组
let lineArr = [];
let lineIndex = -1;
labelDoms.forEach(v => {
if(v.left==left){
lineIndex++;
lineArr[lineIndex] = [];
}
lineArr[lineIndex].push(v);
})
// 超过一行
if(lineArr.length>1){
// 默认显示加载更多按钮
this.showLabel = 1;
const firstTr = lineArr[0].filter(v => (v.right+btnDom.width+(left/15*15)) <= boxDom.width);
this.firstLabel = this.allLabel.slice(0,firstTr.length);
this.labelList = this.firstLabel;
}else{
this.showLabel = 0;
}
},
// 展开
openMore(){
this.showLabel = 2;
this.labelList = this.allLabel;
},
// 收起
closeMore(){
this.showLabel = 1;
this.labelList = this.firstLabel;
}
}
}
wxp.js相关代码
/**
* 延时
* @param {*} delay
*/
export const timeout = delay => new Promise(resolve => setTimeout(resolve, delay));
/**
* 根据ID获取dom的盒模型信息
* @param {*} id
*/
export const getElementById = (id='') => {
return new Promise((resolve, reject) => {
if ((typeof id).toLowerCase() !=='string'){
const err = {
errMsg: '请输入字符串,例如 #box'
}
reject(error(err.errMsg,err));
} else if (id.indexOf('#') < 0) {
const err = {
errMsg: '请输入ID,例如 #box'
}
reject(error(err.errMsg,err));
}else{
var query = wx.createSelectorQuery()
query.select(id).boundingClientRect();
query.selectViewport().scrollOffset();
query.exec(rect => {
if (rect[0]){
let info = rect[0];
info.position = {
left: rect[1].scrollLeft + info.left,
top: rect[1].scrollTop + info.top
};
resolve(info);
}else{
const err = {
errMsg: '没有获取到信息'
}
reject(error(err.errMsg,err));
}
})
}
})
}
/**
* 根据类名获取dom信息
* @param {*} className
*/
export const getElementsByClassName = (className = '') => {
return new Promise((resolve, reject) => {
if ((typeof className).toLowerCase() !== 'string') {
const err = {
errMsg: '请输入字符串,例如 .box'
}
reject(error(err.errMsg,err));
} else if (className.indexOf('.') < 0) {
const err = {
errMsg: '请输入类名,例如 .box'
}
reject(error(err.errMsg,err));
} else {
wx.createSelectorQuery().selectAll(className).boundingClientRect(rects => {
resolve(rects);
}).exec();
}
})
}
作者:不二很纯洁
链接:https://www.jianshu.com/p/87f3c14038a6