小程序标签展开收起功能实现

先看效果 收起时 展开后 主要结构 我用的mpvue,如用原生标签直接转换成原生即可
{{label}}
全部{{allLabel.length}}个
收起
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(); } }) } 做下记录

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):