目录
1、scroll-view 相关问题2、应用场景3、主要属性讲解 3.1,scroll-x、scroll-y,scroll-top、scroll-left、scroll-into-view 3.2,滚动锚定:scroll-anchoring 3.3,upper-threshold、lower-threshold、bindscrolltoupper、bindscrolltolower、bindscroll 3.4,refresher-enabled、refresher-threshold、refresher-triggered、bindrefresherpulling、bindrefresherrefresh、bindrefresherrestore、bindrefresherabort4、示例代码与最佳实践 4.1、示例代码: 4.2、实践建议:5、开发者经常遇到哪些问题? 5.1,使用 scroll-view 时,如何优化使用 setData 向其传递大数据、渲染长列表? 5.2,scroll-view 开启自定义下拉刷新,scroll-view 里面内容太少无法触发刷新? 5.3,scroll-view 在 ios 中下拉刷新,触发两次 bindscrolltoupper 事件? 5.4,scroll-view 组件为什么有时候 scroll-x 不作用? 5.5,scroll-view 中两个 scroll-x 和 scroll-y 同时启用有 bug? 5.6,什么情况下需要使用 scroll-view 的下拉刷新,而不使用页面本身的下拉刷新? 5.7,scroll-view 内不支持嵌套原生组件吗? 5.8、如何实现购物类小程序分类选物品页面?6、如何在小程序中使用 WeUI 组件库?阶段源码参考文献
文 / 石桥码农
==本文约 21126 字,阅读 22 分钟==
说什么真理无穷,进一寸有一寸的欢喜。大家好,我是石桥码农,今天继续为大家分享微信小程序实践相关的技术内容。
一个框架内每个组件的设计,都有设计者的考虑,每个组件都有其特殊的用途。如果说view的存在,主要是为了实现各种常见的ui布局,那么今天分享的,是「三动」容器组件之一的scroll-view。它与movable-view、cover-view,是三动组件,都是为了方便开发者实现特定场景下的特殊业务功能而设计的。
没有这些组件,开发者自己通过view也能实现这些功能;但有了这些组件,实现起来简单了,学习成本也高了。特别当组件的设计过于随心所欲时,学习者的学习负担也更大了
scroll-view是可滚动视图区域组件。这个组件几乎是每一个复杂的多页面小程序都会用的,是使用最广泛的组件之一,但也是在社区被开发者最广为诟病的组件之一。
关于这个组件,有以下几个问题值得思考:
1,当我们说滚动时,涉及到外面的滚动容器与里面的可滚动实体两个对象,我们说滚动到顶部、滚动到底部,指的是什么呢?是什么到顶部,什么到底部了?
2,当滚动事件派发时,滚动到顶部是一个状态,还是一个单一的事件,它会触发多次吗?
3,scrolltoupper事件、scrolltolower事件是什么时候触发的?直接改变scroll-top属性可以触发吗?
4,设置scroll-into-view这个属性,可以将内容盒子滚动到某个子元素处,具体是滚动到哪里呢?如何理解这个属性?
5,如果一个瀑布流页面中有许多图,上面的图比下面的图加载慢,当看到下面图的时候,上面的图突然加载出来,把下面的图挤跑了,这种情况有没有办法解决?是什么技术?
6,有时候在一个后台vue页面中,没有人动它,它自己抖动不止,这可能是什么情况?
7,如何在scroll-view中自定义实现一个下拉刷新交互动画?
8,使用scroll-view实现瀑布流功能时,如果页面比较卡顿,可以朝哪个方向优化?
9,在一些列表中,有时候出于性能考虑,可能需要故意放置一个空白、不显示的子项。空白子项虽然无形中增加了软件包的size,但是也默默提高了性能。
10,在一些购物类或订餐类小程序中,左侧有物品分类,左侧是物品列表,单击分类,右侧自动滚动到相关位置,右侧列表上下滚动,左侧分类菜单自动切换,获得高亮焦点,这样的功能是怎么实现的?
如果这些问题你都比较明白,这个组件相关的内容就没必要看了。
在某购物App上,有这样一个功能:
因为导航按钮太多,产品人员将非常用的按钮放在了第二屏,需向左滚动才可以看到。
在这个地方,有一个实际内容宽度大于手机屏幕的容器,它支持用户用手指左右滑动。下方还有一个滚动提示条,这是根据滚动位置计算出来的。这是自定义实现的效果,相当于浏览器的滚动条,效果是通过css样式控制的。
scroll-view是一个略显复杂的组件。它的属性主要支持了两个功能:左右滚动与下拉更新。
scroll-x、scroll-y默认都是false,不开启滚动。当scroll-y为真时,允许纵向滚动;当添加scroll-x属性时,允许横向滚动。
在这张动图中,上面启用的是scroll-x,下方启用的是scroll-y。由于手机屏幕比较窄,横向滚动需求比较常见。
从实践结果看,scroll-x与scroll-y不是一对互斥的属性,并不是设置了scroll-y,就不能设置scroll-x。两个方向的滚动可以同时开启,但在操作时,只能同时朝一个方向滚动。
scroll-top指内部的滚动实体,高于顶部边缘多少距离。单位默认是px,也可以传入rpx。默认情况下scroll-top是0,当实体向上滚动时,其值慢慢增加。
同理,scroll-left类似。当开启的是横向滚动时,scroll-left是距离左边界、子实体向左滚动的距离。
我们一般说「滚动到顶部、滚动到底部」,指的还不是内部滚动实体滚动到了它所能达到的最大值、最小值,而是指滚动实体顶部边缘到达了滚动外框的顶部,及底滚动实体底部边缘到达了滚动外框的底部。都是以滚动外框为参照物的。
同样scroll-top、scroll-left这两个属性,它们也是以滚动外框的位置为参照物的。
像scroll-top、scroll-left这两个属性,它们是通过属性绑定、控制组件行为的属性。如果我们想让滚动实体滚动到某个位置,并不能直接调用它的一个类似于scrollTo()的方法。我们只能在JS里动态改变scroll-top、scroll-left这两个属性绑定的变量,然后视图渲染后,组件会自动发生滚动。
在vue、小程序中到处都是这样的响应式控制机制,不是直接去调用页面上组件的方法,而只是给组件属性设置一个值,然后静静地等待组件自己更新。
在软件设计中,一般我们为一个对象定义一个类,这个类既有方法,又有属性。我们将这个类实例化,既可以改变实例的属性,又可以调用实例的方法;并且在大多数情况下,我们改变属性时,并不会使实例发生什么行为,而只有明确调用它的方法时,它才会有所动作。
现在在前端这一块,像vue、小程序这样的框架,把这个传统给颠覆了。直接传一个值,让组件自己负责更新,这样看起来更简单。但是在复杂的业务逻辑中,如果能直接能调用组件的方法,可能会更简单一些,因为那样连用于属性绑定的变量都不需要声明了。有时候这种声明是完全没有必要的。
与scroll-top、scroll-left类似的属性,还有scroll-into-view,它用于滚动到某个元素。这个属性很好理解,它的值必须是一个子视图的id,滚动时微信小程序是以子视图的上、左边界为测算依据的。也就是说,纵向滚动,使scroll-top等于子视图的上边界;横向滚动,使scroll-left等于子视图的左边界。
这是一个语法糖属性,它帮助开发者做了一些事情。没有这个属性,我们通过id查找组件,找到组件的上、左边距离上、左滚动边框的距离,通过设置scroll-top、scroll-left属性,同样可以达到目的。
官方文档说,在使用scroll-into-view时,「设置哪个方向可滚动,则在哪个方向滚动到该元素」。
这里有一个问题,前面我们知道了scroll-x、scroll-y这两个布尔属性并不互斥,假如我们同时开启横向、纵向滚动,当通过scroll-into-view向某个子view滚动时,滚动行为是怎么样的?
是先向x方向滚动,还是先向y方向滚动?还是两个方向同时滚动?
答案是小程序错乱了,它既不会同时滚动,也不会先后依次滚动。
程序都是人编出来的,功能也都是有边界的,没有编写过那部分代码,自然也不会那部分功能。
这个属性非常值得一提。它是控制「滚动锚定」特征的,即控制滚动位置不随内容变化而抖动,这种情况据说在用户浏览行为中占比1%。这个属性默认是false,添加后,功能才会开启。
什么是滚动锚定?
假设我们有一个图片瀑布流页面,这样的页面在网站上有许多,随处在一个设计网站上都可以看到。
用户浏览瀑布时,假如由于网速原因,在看下面的图片时,上面的图片突然加载出来。这时候因为上面的图片会使下方的图片自动往下跑。
这个体验肯定很不好。
为了解决这个1%的问题。谷歌提出了「滚动锚定」策略,即通过一个css样式,控制滚动实体在内容变化时不发生滚动。
微信小程序scroll-anchoring这个属性,就是干这个用的。它是一个布尔属性,添加它以后,当上面内容扩充时,微信会自动向上滚动一段扩充的距离。这就是「滚动锚定」策略。不是没有滚动,而是滚动冲抵了,scroll-top已经不一样了。
但是这个属性在某种情况下会给开发者带来意想不到的bug。
vue作为响应式框架,视图自动响应数据更新而重新渲染。假设在某个后台vue项目中,如果恰巧某个滚动实体监听了滚动事件,在滚动发生时自动干了一个改变滚动内容的事。这件事可能很小,只是改变一个边框、或一处字体1个px的大小,但是由于启用了滚动锚定,这个页面可能陷入一种自循环,发生抖动不止的现象。
当出现这样的「抖动永动机」时,简单解决的方法,就是关闭「滚动锚定」策略,或设置一个这样的样式:
overflow-anchor:none;
同时,开启这个策略才可以通过样式开启。scroll-anchoring这个属性,目前小程序只支持iOS手机,在Android手机上需要开发者自己处理。在Android手机上可以添加这样的样式实现相同的功能:
overflow-anchor: auto;
upper-threshold、lower-threshold这两个属性,是用于控制scrolltoupper和scrolltolower事件的,默认都是50px。
当scroll-top小于upper-threshold时,组件派发scrolltoupper事件;同理,当scroll-top小于lower-threshold时,派发scrolltolower事件。这是纵向滚动的情况,当是横向滚动时,是拿scroll-left作为比对值。
这里需要注意,这两个事件不是点事件,而是状态事件。也就是说,upper-threshold为50,当scroll-top小于50时,只要滚动行为在发生,scrolltoupper事件会多次派发。
并且派发的是随心所欲。滚动事件是scroll,并不是scroll派发一次,scrolltoupper派发一次;也不是scroll派发三次或五次,scrolltoupper派发一次。是毫无规律可言。
在这里我们看小程序组件的属性命名,也是随心所欲,毫无章法。
在flex布局里,我们知道当flex-direction的样式值为不同的row或column时,样式值flex-start、flex-end分别也代表了不同的含义。这种思维更像是程序员的思维模式。
但是你看小程序组件的属性是怎么命名的?
scroll-x、scroll-y本应该是两个互斥的属性,结果不互斥;这两个属性应该合并为一个scroll-direction属性,值应该参照css,取row和column。为什么要给程序额外制造心智负担呢?
还有scroll-top、scroll-left,也应该合并为scroll-start。
既然upper-threshold代表了距离顶部 / 左边多远,lower-threshold代表了距离底部 / 右边多远,它俩都是以一抵二的属性,为什么scroll-top、scroll-left,还有scroll-x、scroll-y,要分成两个呢?同一套组件为啥用两套标准?
太随心所欲了。这样的随心所欲,除了增加新手学习的负担,别无用途。怪不得人都说开发难学,一部分门槛是程序员兄弟为自己人量身打造的。
看到这些事件名,就想到没有句读的古文。名称长,没有小驼峰,也没有连字符、下划线分隔,非常不一目了然。
前面三个属性,还有后面四个事件,都是与下拉刷新有关的。刚流行iPhone智能手机的时候,下拉刷新是一个体验亮点。后来这种功能设计渐渐成为了App设计规范。
refresher-enabled用于控制是否开启自定义下拉刷新,默认为false。refresher-threshold是触发下拉更新的临界值,向下拉,松手又回去了,列表没有更新,这是没有达到refresher-threshold的值;达到这个值后,松手是「更新中」的提示。
refresher-triggered这个布尔值,默认为false。它是为了在更新后,取消下拉更新状态的。当组件处于「下拉更新」状态后,它的值变为true,此时程序要去做一些耗时的事情,例如网络加载。待处理完成了,将这个值置为false,下拉更新的状态就恢复回去了。
后面四个事件,是实现自定义下拉动画的关键。
bindrefresherpulling这个事件,是手指按住了,往下拉的过程中派发的。自定义的动画要在这个事件里处理。上面的动画就是自实现的下拉更新动画。
WXS代码:
... onPulling: function(e, instance) { var p = Math.min(e.detail.dy / 80, 1) var icon = instance.selectComponent('#refresherIcon') icon.setStyle({ opacity: p, transform: "rotate(" + (90 + p * 180) + "deg)" }) var view = instance.selectComponent('.refresh-container') view.setStyle({ opacity: p, transform: "scale(" + p + ")" }) if (e.detail.dy >= 80) { if (pullingMessage == "下拉刷新") { pullingMessage = "释放更新" instance.callMethod("setData", { pullingMessage }) } } } }
这段代码稍微有点复杂,主要干了三件事:
1,计算拉到哪了,占总量80的多少,找到icon图标,设置它的旋转角度
2,找到下拉动画的容器,设置它的缩放,看起来越往下拉、容器越大
3,当拉到refresher-threshold临界值时,改变下拉更新的提示文本
这是WXS代码,是在视图层执行的,在这里可以肆意地操作DOM、更新视图,而不用担心因更新渲染开销大。因为它压根儿就不会更新。代码里之所以用callMethod调用页面主体的setData方法,就是为了曲线救国、达到更新视图的目的。
每个WXS代码中的事件句柄函数,执行时都有两个参数传递进来:事件对象与当前页面的实例对象。如果没有这两个参数,这个动画就实现不了啦。默认情况下,WXS在视图层执行,与页面JS中的代码不是一路的,后者是在逻辑层执行的。
如微信官方文档所讲,WXS是一套不一样的脚本语言,它是WeXin Script的简写。WXS 与 JS 是不同的语言,有自己的语法,并不和 JS 一致。
举个例子,在JS中我们一般使用let代表var声明变量,这可以避免因变量作用域不合适而产生奇怪的bug。但是在WXS中,如果我们使用let声明变量的话,微信开发者工具立刻就给我们爆出一个奇怪的bug。
此时代码错乱,无法执行。编辑器会报一个没有什么任何文本提示的错误。这种错误最让人抓狂,毫无征兆、毫无线索,根本无从查证。这个时候只有运气和耐心,还有上帝能帮助自己。
WXS真的是和JS不一样的语言。
我们再看一下bindrefresherrefresh事件。这个事件应该这样读:bind-refresher-refresh,我第一次看到它,就错看成了是err-refresh,以为是发生某个错误时派发的事件,其实不是。
它是组件进入更新中状态时派发的事件。
WXS代码:
onRefresh: function(e, instance) { // 此时手拉开了,进入了加载中的状态 pullingMessage = "更新中" instance.callMethod("setData", { pullingMessage: pullingMessage, refresherTriggered: true }) instance.callMethod("willCompleteRefresh", {})},
在这个地方需要一个定时器模拟网络加载,但是WXS里没有定时器。要么使用页面实例的requestAnimationFrame方法模拟一个定时器,要么在JS中实现。
我选择了后者,这个方案看起来更简单。我在JS中定义了一个willCompleteRefresh方法,然后再在WXS中在合适的时机通过callMethod调用它。
JS代码:
willCompleteRefresh(){ let intervalId = setInterval(()=>{ let pullingMessage = this.data.pullingMessage console.log(pullingMessage,pullingMessage == '更新中') if (pullingMessage.length < 7){ pullingMessage += '.' }else{ pullingMessage = '更新中' } this.setData({ pullingMessage }) },500) setTimeout(()=>{ clearInterval(intervalId) this.setData({ pullingMessage:"已刷新", refresherTriggered:false, }) },2000)},
bindrefresherrestore事件是状态恢复了,是设置了refresher-triggered为false,动画完成之后派发的事件;bindrefresherabort是下拉行为被打断时派发的事件,正常情况下这种事件不会收到。这两个事件属于不可或缺,但不重要的事件。
具体可以看我的阶段性源码,在下方有链接。
关于下拉刷新的组件,有两个开源项目可以参考:
mescroll:github.com/mescroll/mescrollminirefresh:github.com/minirefresh/minirefresh
...{{pullingMessage}} ...
这里用到的mp-icon,是WeUI的icon组件,后面会详细介绍它的使用方法。
JS代码:
// 更新二维数组const updateList = `tabs[${activeTab}].list[${page}]`const updatePage = `tabs[${activeTab}].page`this.setData({ [updateList]: res.data, [updatePage]: page + 1})
wxml:
...
这是微信开发者社区上的一个问题,原问题是「scroll-view 追加数据会自动回到顶部,请问怎样解决?」。
作者可能是想实现一个多tab页的功能,数据是tabs,这是一个大数组。gameListWrap应该是对这个tabs子数据访问的再封装。
在JS代码中,${activeTab}、${page}都是模板字符串中的变量。updateList、updatePage是setData更新时用的key,因为是变量,所以在使用时要用[]括起来。
作者为什么不直接使用push方法呢?例如:
let tabData = this.data.tabs[activeTab]tabData.list.push(res.data)tabData.page = page+1let key = `tabs[${activeTab}]`this.setData({ [key]: tabData})
当有新数据进来时,直接往某个tab页数据的底部推入新数据。
但这种操作有一个问题。setData受限于视图层与逻辑层之间用于传话的evaluateJavascript函数,其每次携带的数据大小,官方评测标准要求在文本序列化以后大小不能超过256KB。如果某个tab页是一个瀑布流,其tabData.list可能是一个越来越大的数据,不超过256KB是很难的。
这就犯了「每次 setData 都传递大量新数据」的忌讳,是不被微信小程序官方建议的。
虽然传递的不全是新数据,但微信小程序不知道哪些是新的,哪些是旧的,凡是在list中传递过来的,它都认为是新数据。
那么这个问题如何解决呢?如何再优化一下呢?方法是只更新新数据,可以参照作者在实践过程中找到的解决方法。代码:
const updateListStr = `gameListData[${activeTab}][${page}]`const updatePageStr = `pages[${activeTab}]`this.setData({ [updateListStr]: res, [updatePageStr]: page + 1})
将tab数据与页面数据分开。在循环渲染时,按照pages[activeTab].page循环;取数据时,依照page当前的值,从gameListData[activeTab]中查取。gameListData此时在形式上是一个数组,但实际上相当于是一个map。
在渲染长列表时,微信给出了一个长列表组件recycle-view:
developers.weixin.qq.com/miniprogram/dev/extended/component-plus/recycle-view.html
用于渲染无限长的列表。实现原理也很简单,通过监听scroll事件,只渲染当前视图窗口内的list列表,看不见的地方用空的占位符代替。
我在vue项目中曾实现过一个类似的长列表组件,以前推过文章,可以在这里查看:
v-if 条件渲染与 v-for 列表渲染:mp.weixin.qq.com/s/llWnXmMKpXyayK860n1iLQ
不知道这个问题我讲明白没有,从后端拉取大数据渲染长列表时,现在你明白应该怎么做了吗?
关键是明白卡顿并不定是手机真卡了,并不一定是GPU运转不过来了,而是视图渲染不及时。我们看到页面卡顿时,可能GPU空闲率有90%。
影响小程序渲染效率的罪魁祸首是evaluateJavascript这个底层通讯函数,它是逻辑层与视图层之间一个很小的独立桥,无法承接过大、过快的派遣。
这个问题在旧的基础库版本中存在,经测试在新的2.10.4版本下该问题已经解决了。
之所以出现这个问题,是因为scroll-view组件所有事件,除了scroll本身,都是scroll事件的次生代事件。也就是说,像refresher开头的事件是以scroll事件为基础,在内部做了计算之后派发的。
内容太少,根本无法触发scroll事件,还怎么触发下拉更新呢?
在新的基础库版本中虽然解决了这个问题,但是当内容少的时候,却是连页面内容也滑动了。这是可以理解的,因为除了在父容器上监听scroll事件,可能也没有其它的解决方法了。
问题是解决了,但牺牲了一些性能。如果内容少,建议直接添加一个看不见的容器,使内容高度一定大于滚动框架的高度,就没有这个问题了。
在一些展示列表中,开始的时候可能只有一二个子项,这个时候也想触发下拉更新,合适的做法是在列表里故意放一个无用的空项。看以无用,实则有用。
这个问题前面讲过了,scrolltoupper是scroll的次生代事件,是状态事件,不是单点事件,存在多次派发的情况。这种情况只能自己在业务逻辑中做一些特别的防抖动处理。
有时候是鼠标无法滑动,在mac电脑上,用触控板就可以滑动。
如果不是这个问题,可以考虑以下三点:
这里有一个延伸问题,white-space设置为nowrap好理解,是不换行;display设置为inline-block是什么意思呢?为什么不设置为block或inline呢?
block是块元素样式,将组件设置为块元素,可以设置它的宽、度、margin、padding等值。block会自动换行。inline是内联元素样式,容器设置为inline后,子元素将在一行内显示、不换行。inline-block兼具两者优势,子元素既在一行内显示、不换行,又能设置其宽、高等块元素属性。
举个例子,ul的li默认是自上而下换行显示的,如果给ul添加display:inline-block,所有li会排行成一行。
理解了inline-block样式值的作用,回头再看为什么添加display:inline-block这个样式,就好理解了。
据描述现象是这样的:苹果iOS手机正常,在安卓手机上乱跳。
不要同时启用这两个属性。他们虽然形式上不是互斥的,但实际上却是互斥的。这是架构师在框架设计上的疏忽。
除了使用scroll-view的下拉刷新,有一种替代方案,是直接使用Page的下拉刷新。如何使用呢?我们看一下。
很简单,在app.json的window选项中或页面配置中,开启enablePullDownRefresh。通过wx.startPullDownRefresh触发下拉刷新,此时页面将处于「更新中」的状态。当处理完异步加载后,使用wx.stopPullDownRefresh停止更新状态。
并且,在滚动 scroll-view 时,小程序会阻止页面回弹;在 scroll-view 中滚动,无法触发 onPullDownRefresh事件。基于此有人建议,尽量不要使用scroll-view的下拉刷新,直接使用Page的就可以了。
但是,有时候必须基于scroll-view实现局部页面的刷新,这种情况是很普遍的。
在顶部自定义一个navigatorBar导航栏,单击一个按钮切换到一个页面,每个页面都是一个独立的scroll-view组件。这时候下拉刷新使用page整体的就不合适了,下拉刷新的动画必须出现在navigatorBar下方才合理。这时候就必然用到scroll-view的自定义下拉刷新功能了。
使用自定义刷新功能,scroll-view需要一个固定的高度,这个高度需要我们自己计算。
通过wx.getSystemInfo可以获取到两个屏幕高度:screenHeight和windowHeight,前者是屏幕高度,是手机上会亮的那块玻璃板的高度;后者是一个计算值,是screenHeight减去系统状态栏——有电量提示、wifi信号的那一栏(statusBarHeight)、再减去导航栏——有标题和胶囊按钮的那一栏、再减去微信自带的tabBar组件的高度,之后得到的才是windowHeight,是可用的窗口高度。
如果页面配置启用了navigationStyle:"custom",开发者自定义页面导航栏,则导航栏高度不会在windowHeight中减去;还有,如果某个页面没有启用tabBar,高度又会增大一些。这些我们都要注意。
拿到windowHeight之后,它还不是scroll-view应有的高度,因为页面上还可能有自定义的底部导航栏、顶部导航栏,这些高度也要减去。
因为这些原因,给scroll-view设置高度,在不同页面是不一样的,必须区别对待。
不支持也情有可原,因为要滚动,普通组件与原生组件都不在一个层,一个在上面,一个在下面,怎么同步?
网上有人说,小程序scroll-view不支持嵌套textarea等组件,那是旧版本。网上有许多教程是旧的。
从基础库2.4开始,已经开始支持嵌套textarea、map、canvas、video 这些原生组件了。其它原生组件不支持。支持的越多功能越完备,学习起来越复杂,性能流失也越大。
这里主要需要实现两个功能:
第一个功能点,可以通过scroll-into-view属性实现,将左侧菜单与右侧每块区域的id对应起来,单击时更新scroll-into-view绑定的id。
wxml代码:
{{item.name}}
{{item.name}}
JS代码:
// 单击左侧菜单menuListOnClick:function(e){ let me=this; me.setData({ activeViewId:e.target.id, currentIndex:e.target.dataset.index })}// 滚动时触发,计算当前滚动到的位置对应的菜单是哪个scrollFunc:function(e){ this.setData({ scrollTop:e.detail.scrollTop }) for (let i = 0; i < this.data.heightList.length; i++) { let height1 = this.data.heightList[i]; let height2 = this.data.heightList[i + 1]; if (!height2 || (e.detail.scrollTop >= height1 && e.detail.scrollTop < height2)) { this.setData({ currentIndex: i }) return; } } this.setData({ currentIndex: 0 })}
第二个功能点,是通过计算实现的。在列表数据绑定时,把右侧每块物品区域的高度记录下来,就是上面代码中的heightList。右侧列表滚动时,通过绑定scroll事件,拿到scrollTop,循环对比在哪个区域,就把哪个区域对应的菜单高亮。
首先,从网站上下载组件包:
developers.weixin.qq.com/miniprogram/dev/extended/weui/download.html
微信提供了按需下载的功能,可以只下载自己用到的组件。
接着解压组件包,将解压到的目录weui-miniprogram复制到项目根目录下。如果开启了云开发,一般为miniprogram目录。
再着,在app.wxss里面引入weui.wxss:
@import './weui-miniprogram/weui-WXSs/dist/style/weui.wxss'
可以将这句代码直接拷贝到app.wxss文件内。这是WeUI组件库的样式表。没有没有这个文件,组件不能正常显示。
再着,在哪个页面引用什么组件,就在它的json配置文件中添加usingComponents组件使用声明。以icon为例:
{ "usingComponents": { "mp-icon": "./weui-miniprogram/icon/icon" }}
mp-icon是完全可以自定义的。WeUI组件库默认以mp开头。
最后,在wxml页面中使用组件:
icon属性是图标类型。具体有哪些类型可以使用,可以在这个网址查看:
developers.weixin.qq.com/miniprogram/dev/extended/weui/icon.html
这个页面上有一个icon列表,列表里的图标名称都可以使用。
注意:mp-icon的颜色不能从父组件直接继承,所以即使父组件已经设置了颜色,这个组件也需要额外通过color属性再设置一次。还有,原生icon组件控制类型的属性名称是type,但是这个mp-icon,控制类型的却是icon。
源码链接:git.weixin.qq.com/rxyk/weapp-practice/repository/archive.zip?ref=2.3-scroll-view
与本文相关的代码主要位于:miniprogram/pages/2.3/scroll-view
这篇文章是「微信小程序开发实践2020」专栏的一部分。
好了,我是石桥码农,今天分享就到这里,今天主要讲了scroll-view这个组件,希望对你的学习有帮助。有什么问题欢迎留言,也欢迎进群讨论。
2020年4月10日
真正的勇士,是看清生活的真想后,依然热爱生活。
书山有路勤为径,艺海无涯苦作舟。
说什么真理无穷,进一寸有一寸的欢喜。
不能穷尽所有,但要尽己所能。