小程序标签展开收起功能实现
先看效果
收起时
展开后
主要结构
我用的mpvue,如用原生标签直接转换成原生即可
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();
}
})
}
做下记录
{{label}}
全部{{allLabel.length}}个
收起
发表评论 (审核通过后显示评论):