前几天公司项目要修改一下,商品的布局,我看了一眼UI,发现是瀑布流的效果,之前我一直没做过这样的效果,当时脑海里就在思考,要怎么实现这个效果。瀑布流的效果和之前高度确定的效果区别就是,每个商品的高度都是不确定的,都会受到商品的标题长度或者是有无商品标签的影响,这样的话,就不能直接往响应式的数据中直接push数据。
思路:创建两个响应式数组leftArr,rightArr,用于存放商品数据,然后根据渲染好的左右两侧的商品列表的节点的高度进行比较,哪边高度小,对应后端获取的商品就加到哪边(记住是一个一个的加,即push)。
再做这前,我也去看了下插件市场中有没有瀑布流的布局,发现也有很多。下载下来看了一下,就只有一个人的代码是和我想的一样的。其他的都不合适,有些就是一个假的瀑布流效果,既然有人和我想法差不多,我就直接拷贝有用的代码,组织好后用到自己的项目中。其中有一个兼容性的问题:就是nextTick。
因为我们要获取节点对象的高度,而数据要push到leftArr或rightArr数组中,那么就需要等待页面更新以后再去计算节点的高度,经过测试 微信小程序端,支付宝小程序端,APP Android和IOS都不行,又很无奈的用延时去做。setTimeout延时0ms,微信小程序端,支付宝小程序端可以,APP Android和IOS都不行。那只能再把时间调就一点 20ms,经过多次测试,基本上没发现什么问题,这样瀑布流的效果就完成了。如果大家有更好的想法,一定要分享给我哦。
那么下一步就去做成一个组件,这样就可以在多个页面复用了,待组件写好后,进行测试,发现就微信小程序端拿不到对应的节点信息,那么对应的高度也就没有,完蛋。。。。其他端都是可以正常使用的。不知道是uniapp的bug,还是微信小程序的bug。无奈只能把逻辑代码写到一个js里,页面就不能复用了。
/* 堆代码 duidaima.com */ /* 获取元素的高度 */ handleDomHeight(el) { return new Promise((resolve, reject) => { let dom = uni.createSelectorQuery().select('#' + el) dom.boundingClientRect(res => { resolve(res.height); }).exec(); }) } /** 瀑布流赋值操作 * @param {Object} dom1 左侧的节点id * @param {Object} dom2 有侧的节点id * @param {Object} list1 左侧的商品数据 * @param {Object} list2 右侧的商品数据 * @param {Object} originalList 源数据,即后端返回的数据 */ handleWaterfallFlowLayout(dom1, dom2, list1, list2, originalList) { let leftHeight = 0; let rightHeight = 0; let item = originalList.shift(); if (!item) return; this.handleDomHeight(dom1).then(res => { leftHeight = res; this.handleDomHeight(dom2).then(res2 => { rightHeight = res2; // console.log(leftHeight, rightHeight); if (rightHeight >= leftHeight) { list1.push(item); // console.log('left'); } else { list2.push(item); // console.log('right'); } // Vue.nextTick(() => { // }) // #ifndef APP-PLUS setTimeout(() => { this.handleWaterfallFlowLayout(dom1, dom2, list1, list2, originalList); }); //#endif // #ifdef APP-PLUS setTimeout(() => { this.handleWaterfallFlowLayout(dom1, dom2, list1, list2, originalList); }, 20); /* app端 延时20ms是关键 this.nextTick无效*/ //#endif }) }) }
下面还是让我们看看效果吧: