专栏首页艺述思维微信小程序实践:2.3 可滚动的容器组件之 scroll-view

微信小程序实践:2.3 可滚动的容器组件之 scroll-view

目录

1、scroll-view 相关问题
2、应用场景
3、主要属性讲解
  3.1,scroll-x、scroll-y,scroll-top等
  3.2,滚动锚定:scroll-anchoring
  3.3,upper-threshold、lower-threshold等
  3.4,refresher-enabled、refresher-threshold等
4、示例代码与最佳实践
  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-viewcover-view,是三动组件,都是为了方便开发者实现特定场景下的特殊业务功能而设计的。

没有这些组件,开发者自己通过view也能实现这些功能;但有了这些组件,实现起来简单了,学习成本也高了。特别当组件的设计过于随心所欲时,学习者的学习负担也更大了

1、scroll-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,在一些购物类或订餐类小程序中,左侧有物品分类,左侧是物品列表,单击分类,右侧自动滚动到相关位置,右侧列表上下滚动,左侧分类菜单自动切换,获得高亮焦点,这样的功能是怎么实现的?

如果这些问题你都比较明白,这个组件相关的内容就没必要看了。

2、应用场景

在某购物App上,有这样一个功能:

因为导航按钮太多,产品人员将非常用的按钮放在了第二屏,需向左滚动才可以看到。

在这个地方,有一个实际内容宽度大于手机屏幕的容器,它支持用户用手指左右滑动。下方还有一个滚动提示条,这是根据滚动位置计算出来的。这是自定义实现的效果,相当于浏览器的滚动条,效果是通过css样式控制的。

3、主要属性讲解

scroll-view是一个略显复杂的组件。它的属性主要支持了两个功能:左右滚动与下拉更新。

3.1,scroll-x、scroll-y,scroll-top、scroll-left、scroll-into-view

scroll-x、scroll-y默认都是false,不开启滚动。当scroll-y为真时,允许纵向滚动;当添加scroll-x属性时,允许横向滚动。

在这张动图中,上面启用的是scroll-x,下方启用的是scroll-y。由于手机屏幕比较窄,横向滚动需求比较常见。

从实践结果看,scroll-xscroll-y不是一对互斥的属性,并不是设置了scroll-y,就不能设置scroll-x。两个方向的滚动可以同时开启,但在操作时,只能同时朝一个方向滚动。

scroll-top指内部的滚动实体,高于顶部边缘多少距离。单位默认是px,也可以传入rpx。默认情况下scroll-top0,当实体向上滚动时,其值慢慢增加。

同理,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方向滚动?还是两个方向同时滚动?

答案是小程序错乱了,它既不会同时滚动,也不会先后依次滚动。

程序都是人编出来的,功能也都是有边界的,没有编写过那部分代码,自然也不会那部分功能。

3.2,滚动锚定 scroll-anchoring

这个属性非常值得一提。它是控制「滚动锚定」特征的,即控制滚动位置不随内容变化而抖动,这种情况据说在用户浏览行为中占比1%。这个属性默认是false,添加后,功能才会开启。

什么是滚动锚定?

假设我们有一个图片瀑布流页面,这样的页面在网站上有许多,随处在一个设计网站上都可以看到。

用户浏览瀑布时,假如由于网速原因,在看下面的图片时,上面的图片突然加载出来。这时候因为上面的图片会使下方的图片自动往下跑。

这个体验肯定很不好。

为了解决这个1%的问题。谷歌提出了「滚动锚定」策略,即通过一个css样式,控制滚动实体在内容变化时不发生滚动

微信小程序scroll-anchoring这个属性,就是干这个用的。它是一个布尔属性,添加它以后,当上面内容扩充时,微信会自动向上滚动一段扩充的距离。这就是「滚动锚定」策略。不是没有滚动,而是滚动冲抵了,scroll-top已经不一样了。

但是这个属性在某种情况下会给开发者带来意想不到的bug

vue作为响应式框架,视图自动响应数据更新而重新渲染。假设在某个后台vue项目中,如果恰巧某个滚动实体监听了滚动事件,在滚动发生时自动干了一个改变滚动内容的事。这件事可能很小,只是改变一个边框、或一处字体1px的大小,但是由于启用了滚动锚定,这个页面可能陷入一种自循环,发生抖动不止的现象。

当出现这样的「抖动永动机」时,简单解决的方法,就是关闭「滚动锚定」策略,或设置一个这样的样式:

overflow-anchor:none;

同时,开启这个策略才可以通过样式开启。scroll-anchoring这个属性,目前小程序只支持iOS手机,在Android手机上需要开发者自己处理。在Android手机上可以添加这样的样式实现相同的功能:

overflow-anchor: auto;

3.3,upper-threshold、lower-threshold、bindscrolltoupper、bindscrolltolower、bindscroll

upper-threshold、lower-threshold这两个属性,是用于控制scrolltoupperscrolltolower事件的,默认都是50px

scroll-top小于upper-threshold时,组件派发scrolltoupper事件;同理,当scroll-top小于lower-threshold时,派发scrolltolower事件。这是纵向滚动的情况,当是横向滚动时,是拿scroll-left作为比对值。

这里需要注意,这两个事件不是点事件,而是状态事件。也就是说,upper-threshold50,当scroll-top小于50时,只要滚动行为在发生,scrolltoupper事件会多次派发。

并且派发的是随心所欲。滚动事件是scroll,并不是scroll派发一次,scrolltoupper派发一次;也不是scroll派发三次或五次,scrolltoupper派发一次。是毫无规律可言。

在这里我们看小程序组件的属性命名,也是随心所欲,毫无章法。

flex布局里,我们知道当flex-direction的样式值为不同的rowcolumn时,样式值flex-startflex-end分别也代表了不同的含义。这种思维更像是程序员的思维模式。

但是你看小程序组件的属性是怎么命名的?

scroll-x、scroll-y本应该是两个互斥的属性,结果不互斥;这两个属性应该合并为一个scroll-direction属性,值应该参照css,取rowcolumn。为什么要给程序额外制造心智负担呢?

还有scroll-topscroll-left,也应该合并为scroll-start

既然upper-threshold代表了距离顶部 / 左边多远,lower-threshold代表了距离底部 / 右边多远,它俩都是以一抵二的属性,为什么scroll-top、scroll-left,还有scroll-x、scroll-y,要分成两个呢?同一套组件为啥用两套标准?

太随心所欲了。这样的随心所欲,除了增加新手学习的负担,别无用途。怪不得人都说开发难学,一部分门槛是程序员兄弟为自己人量身打造的。

3.4,refresher-enabled、refresher-threshold、refresher-triggered、bindrefresherpulling、bindrefresherrefresh、bindrefresherrestore、bindrefresherabort

看到这些事件名,就想到没有句读的古文。名称长,没有小驼峰,也没有连字符、下划线分隔,非常不一目了然。

前面三个属性,还有后面四个事件,都是与下拉刷新有关的。刚流行iPhone智能手机的时候,下拉刷新是一个体验亮点。后来这种功能设计渐渐成为了App设计规范。

refresher-enabled用于控制是否开启自定义下拉刷新,默认为falserefresher-threshold是触发下拉更新的临界值,向下拉,松手又回去了,列表没有更新,这是没有达到refresher-threshold的值;达到这个值后,松手是「更新中」的提示。

refresher-triggered这个布尔值,默认为false。它是为了在更新后,取消下拉更新状态的。当组件处于「下拉更新」状态后,它的值变为true,此时程序要去做一些耗时的事情,例如网络加载。待处理完成了,将这个值置为false,下拉更新的状态就恢复回去了。

后面四个事件,是实现自定义下拉动画的关键。

bindrefresherpulling这个事件,是手指按住了,往下拉的过程中派发的。自定义的动画要在这个事件里处理。上面的动画就是自实现的下拉更新动画。

WXS代码:

<wxs module="refresh">    ...    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          })        }      }    }  }</wxs>

这段代码稍微有点复杂,主要干了三件事:

1,计算拉到哪了,占总量80的多少,找到icon图标,设置它的旋转角度

2,找到下拉动画的容器,设置它的缩放,看起来越往下拉、容器越大

3,当拉到refresher-threshold临界值时,改变下拉更新的提示文本

这是WXS代码,是在视图层执行的,在这里可以肆意地操作DOM、更新视图,而不用担心因更新渲染开销大。因为它压根儿就不会更新。代码里之所以用callMethod调用页面主体的setData方法,就是为了曲线救国、达到更新视图的目的。

每个WXS代码中的事件句柄函数,执行时都有两个参数传递进来:事件对象与当前页面的实例对象。如果没有这两个参数,这个动画就实现不了啦。默认情况下,WXS视图层执行,与页面JS中的代码不是一路的,后者是在逻辑层执行的。

如微信官方文档所讲,WXS是一套不一样的脚本语言,它是WeXin Script的简写。WXSJS是不同的语言,有自己的语法,并不和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-triggeredfalse,动画完成之后派发的事件;bindrefresherabort是下拉行为被打断时派发的事件,正常情况下这种事件不会收到。这两个事件属于不可或缺,但不重要的事件。

具体可以看我的阶段性源码,在下方有链接。

关于下拉刷新的组件,有两个开源项目可以参考:

mescroll:github.com/mescroll/mescrollminirefresh:github.com/minirefresh/minirefresh

4、示例代码与最佳实践

4.1、示例代码

<wxs module="refresh">  ...</wxs><scroll-view scroll-y   style="width: 100%; height: 400px;overflow-anchor:auto;"   scroll-with-animation   enable-back-to-top enable-flex   scroll-anchoring refresher-enabled   refresher-default-style="none"   refresher-background="#FFF"   bindrefresherpulling="..."   refresher-triggered="{{refresherTriggered}}">  <view slot="refresher" class="refresh-container"      style="display: block; width: 100%; height: 80px; background: #F8f8f8; display: flex; align-items: center;">    <view class="view1" style="text-align: center; width: 100%;display:flex;align-items:center;justify-content:center;color:#888;">      <mp-icon id="refresherIcon" icon="arrow" color="#888" size="{{20}}" style="margin-right:5px;transform:rotate(90deg)"></mp-icon>      <text style="min-width:80px;text-align:left;">{{pullingMessage}}</text>    </view>  </view>  ...</scroll-view>

这里用到的mp-icon,是WeUIicon组件,后面会详细介绍它的使用方法。

4.2、最佳实践

  1. 启用scroll-anchoring,同时添加overflow-anchor:auto样式,应对Android机型
  2. 只开启一个方向的滚动,scroll-yscroll-x只取其一。当开启scroll-y时,必须给组件一个高度,例如400px,或其它值;当启用scroll-x时,必须给组件一个宽度,一般这个值是100%,取屏幕宽度。
  3. 开启enable-flex,这个属性是启用flexbox布局的,相当于添加display:flex样式。但是如果是自己添加,是加在了外围容器上,只有通过这个属性添加,才能加到内围真正的容器上。这是个复杂的容器。
  4. 当需要时,使用refresher-enabled启用下拉动画的自定义。自定义可以很方便地实现这样的小人跑动动画:

自定义的代码最好在WXS中实现,以bindrefresher开头的事件句柄都在WXS中定义。这可以提高渲染效率,减少页面卡顿。

  1. 下拉动画组件的背景色用#F8f8f8,前景色——包括图标与文本,用#888,这更符合微信设计规范。
  2. 在下拉动画组件中,可以启用flexbox布局,参见上面的WXSS代码。这容易使图标、文本上、下、左、右居中。
  3. 在自定义下拉动画时,容器的slot要标记为refresher,虽然官方文档没有这样写,但如果你不这样做,你的自定义下拉动画是拒绝工作的。
  4. 尽量不要在JS代码中,在scroll事件句柄中,直接更新视图,把相关的频繁的更新视图的代码,放在WXS模块中。在大列表视图中尤其要如此。
  5. 在启用scroll-x时,一般设置宽度为100%,横向满屏。如果出现不滚动的现象,可以尝试给外框容器添加样式:white-space:nowrap;display:inline-block,并且保证内容的实际宽度大于屏幕宽度。

5、开发者经常遇到哪些问题?

5.1,使用 scroll-view 时,如何优化使用 setData 向其传递大数据、渲染长列表?

JS代码:

// 更新二维数组const updateList = `tabs[${activeTab}].list[${page}]`const updatePage = `tabs[${activeTab}].page`this.setData({    [updateList]: res.data,    [updatePage]: page + 1})

wxml

<view wx:for="{{gameListWrap}}" wx:for-item="gameList">   ...</view>

这是微信开发者社区上的一个问题,原问题是「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项目中曾实现过一个类似的长列表组件,以前推过文章,可以在这里查看:15 v-if 条件渲染与 v-for 列表渲染

不知道这个问题我讲明白没有,从后端拉取大数据渲染长列表时,现在你明白应该怎么做了吗?

关键是明白卡顿并不定是手机真卡了,并不一定是GPU运转不过来了,而是视图渲染不及时。我们看到页面卡顿时,可能GPU空闲率有90%

影响小程序渲染效率的罪魁祸首是evaluateJavascript这个底层通讯函数,它是逻辑层与视图层之间一个很小的独立桥,无法承接过大、过快的派遣。

5.2,scroll-view 开启自定义下拉刷新,scroll-view 里面内容太少无法触发刷新?

这个问题在旧的基础库版本中存在,经测试在新的2.10.4版本下该问题已经解决了。

之所以出现这个问题,是因为scroll-view组件所有事件,除了scroll本身,都是scroll事件的次生代事件。也就是说,像refresher开头的事件是以scroll事件为基础,在内部做了计算之后派发的。

内容太少,根本无法触发scroll事件,还怎么触发下拉更新呢?

在新的基础库版本中虽然解决了这个问题,但是当内容少的时候,却是连页面内容也滑动了。这是可以理解的,因为除了在父容器上监听scroll事件,可能也没有其它的解决方法了。

问题是解决了,但牺牲了一些性能。如果内容少,建议直接添加一个看不见的容器,使内容高度一定大于滚动框架的高度,就没有这个问题了。

在一些展示列表中,开始的时候可能只有一二个子项,这个时候也想触发下拉更新,合适的做法是在列表里故意放一个无用的空项。看以无用,实则有用。

5.3,scroll-view 在 ios 中下拉刷新,触发两次 bindscrolltoupper 事件?

这个问题前面讲过了,scrolltoupperscroll的次生代事件,是状态事件,不是单点事件,存在多次派发的情况。这种情况只能自己在业务逻辑中做一些特别的防抖动处理。

5.4,scroll-view 组件为什么有时候 scroll-x 不作用?

有时候是鼠标无法滑动,在mac电脑上,用触控板就可以滑动。

如果不是这个问题,可以考虑以下三点:

  1. 内容宽度是否大于外框容器宽度
  2. 可以给外框添加white-space:nowrap;display:inline-block样式,看能否解决
  3. 如果内容使用flexbox布局,要确保scroll-view组件启用了enable-flex属性。启用enable-flex属性,与直接添加display:inline-block并不冲突。

这里有一个延伸问题,white-space设置为nowrap好理解,是不换行;display设置为inline-block是什么意思呢?为什么不设置为blockinline

block是块元素样式,将组件设置为块元素,可以设置它的宽、度、marginpadding等值。block会自动换行。inline是内联元素样式,容器设置为inline后,子元素将在一行内显示、不换行。inline-block兼具两者优势,子元素既在一行内显示、不换行,又能设置其宽、高等块元素属性。

举个例子,ulli默认是以上而下换行显示的,如果给ul添加display:inline-block,所有li会排行成一行。

理解了inline-block样式值的作用,回头再看为什么添加display:inline-block这个样式,就好理解了。

5.5,scroll-view 中两个 scroll-x 和 scroll-y 同时启用有 bug?

据描述现象是这样的:苹果iOS手机正常,在安卓手机上乱跳。

不要同时启用这两个属性。他们虽然形式上不是互斥的,但实际上却是互斥的。这是架构师在框架设计上的疏忽。

5.6,什么情况下需要使用 scroll-view 的下拉刷新,而不使用页面本身的下拉刷新?

除了使用scroll-view的下拉刷新,有一种替代方案,是直接使用Page的下拉刷新。如何使用呢?

很简单,在app.jsonwindow选项中或页面配置中,开启enablePullDownRefresh。通过wx.startPullDownRefresh触发下拉刷新,此时页面将拉于「更新中」的状态。当处理完异步加载后,使用wx.stopPullDownRefresh停止更新状态。

并且,在滚动scroll-view时,小程序会阻止页面回弹;在scroll-view中滚动,无法触发onPullDownRefresh事件。基于此有人建议,尽量不要使用scroll-view的下拉刷新。

但是,有时候必须基于scroll-view实现局部页面的刷新,这种情况是很普遍的。

在顶部自定义一个navigatorBar导航栏,单击一个按钮切换到一个页面,每个页面都是一个独立的scroll-view组件。这时候下拉刷新使用page整体的就不合适了,下拉刷新的动画必须出现在navigatorBar下方才合理。这时候就必然用到scroll-view的自定义下拉刷新功能了。

scroll-view需要一个固定的高度,如果要自定义实现下拉刷新,这个高度需要我们自己计算。

通过wx.getSystemInfo可以获取到两个屏幕高度:screenHeightwindowHeight,前者是屏幕高度,是手机上会亮的那块玻璃板的高度;后者是一个计算值,是screenHeight减去系统状态栏——有电量提示、wifi信号的那一栏(statusBarHeight)、再减去导航栏——有标题和胶囊按钮的那一栏、再减去微信自带的tabBar组件的高度,之后得到的才是windowHeight,是可用的窗口高度。

如果页面配置启用了navigationStyle:"custom",开发者自定义页面导航栏,则导航栏高度不会在windowHeight中减去;还有,如果某个页面没有启用tabBar,高度又会增大一些。

拿到windowHeight之后,它还不是scroll-view应有的高度,因为页面上还可能有自定义的底部导航栏、顶部导航栏,这些高度也要减去。

因为这些原因,给scroll-view设置高度,在不同页面是不一样的,必须区别对待。

5.7,scroll-view 内不支持嵌套原生组件吗?

不支持也情有可原,因为要滚动,普通组件与原生组件都不在一个层,一个要上面,一个要下面,怎么同步?

网上有人说,小程序scroll-view不支持嵌套textarea等组件,那是旧版本。网上有许多教程是旧的。

从基础库2.4开始,已经开始支持嵌套textareamapcanvasvideo这些原生组件了。其它原生组件不支持。支持的越多功能越完备,学习越复杂,性能流失也越大。

5.8、如何实现购物类小程序分类选物品页面?

这里主要需要实现两个功能:

  1. 单击左侧菜单,右侧滚动到相应位置
  2. 在右侧滚动,左侧菜单自动同步高亮

第一个功能点,可以通过scroll-into-view属性实现,将左侧菜单与右侧每块区域的id对应起来,单击时更新scroll-into-view绑定的id

wxml代码:

<!-- 左侧菜单 --><scroll-view scroll-y='true' class='nav'>  <view wx:for='{{list}}'  wx:key='{{item.id}}' id='{{item.id}}'        class='navList {{currentIndex==index?"active":""}}' bindtap="menuListOnClick" data-index='{{index}}'>{{item.name}}</view></scroll-view><!-- 右侧内容 --><scroll-view scroll-y='true' scroll-into-view='{{activeViewId}}' bindscroll='scrollFunc'>  <view class="fishList" wx:for='{{content}}' id='{{item.id}}' wx:key='{{item.id}}'>    <p>{{item.name}}</p>  </view></scroll-view>

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,循环对比在哪个区域,就把哪个区域对应的菜单高亮。

6、如何在小程序中使用 WeUI 组件库?

首先,从网站上下载组件包:

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页面中使用组件:

<mp-icon icon="arrow"></mp-icon>

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日

参考文献

  • WEIXIN."cover-view".[Online]Available:developers.weixin.qq.com/miniprogram/dev/component/cover-view.html(2020.04).
  • WEIXIN."movable-view".[Online]Available:developers.weixin.qq.com/miniprogram/dev/component/movable-view.html(2020.04).
  • WEIXIN."scroll-view".[Online]Available:developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html(2020.04).
  • "滚动锚定".[Online]Available:cnblogs.com/ziyunfei/p/6668101.html(2017.04).
  • "下载WeUI组件库".[Online]Available:developers.weixin.qq.com/miniprogram/dev/extended/weui/download.html
  • WXS.[Online]Available:developers.weixin.qq.com/miniprogram/dev/framework/view/WXS/
  • Element.scrollIntoView().[Online]Available:developer.mozilla.org/zh-CN/docs/Web/API/Element/scrollIntoView
  • 「scroll-view 追加数据会自动回到顶部」.[Online]Available:developers.weixin.qq.com/community/develop/doc/0004c47ef14280f7101ab161151800
  • 「scroll-view 开启自定义下拉刷新」.[Online]Available:developers.weixin.qq.com/community/develop/doc/0000685ea0cd78c16ef9fd3a65b800
  • 「scroll-view ios中下拉刷新触发两次bindscrolltouppe」.[Online]Available:developers.weixin.qq.com/community/develop/doc/0004667f0e0ad8da7de8adc9e56000?_at=1586743575247
  • 「scroll-view 组件为什么 scroll-x 不启用」.[Online]Available:developers.weixin.qq.com/community/develop/doc/0006cae8ce02e0b29548700385b800?_at=1586763031642
  • 「微信小程序的高度和scroll-view」.[Online]Available:jianshu.com/p/198c62482afa
  • 「CSS中display:inline-block的作用」.[Online]Available:jianshu.com/p/22c4bae88566
  • 「微信小程序scroll-view详解及案例」.[Online]Available:segmentfault.com/a/1190000021223987

本文分享自微信公众号 - 用故事讲技术(ygsjjs),作者:石桥码农

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • React全家桶简介

    当前前端开发已经进入以vue、react、webpack为代表的编程2.0时代。在1.0时代,代码是写给机器的;在2.0时代,代码是写给工具的,然后由工具处理后...

    李艺
  • 关于 Serverless 的清单分享

    “ 中台和Serverless是今年最火的两个技术名词了,程序员不得不了解一下。短期来看,运维会失业或转行;长远来看,程序员将分化。未来编程可能极其简单。”

    李艺
  • 小程序开发实践:视图容器 view介绍,使用 view 搞定所有常见的 UI 布局

    view 是最基础的,也是微信小程序第一个公布的容器组件。所谓容器组件,就像 HTML 里的 div 标签一样,是为容纳其它组件而存在的。它本身也可以有一些自己...

    李艺
  • 微信小程序示例 - 视图容器

    滑块视图容器 swiper ? 代码结构 <swiper> <swiper-item> <image src="{{item}}"/> ...

    dys
  • 忍法,scroll 翻滚之术!

    但其实随着时间的推移, web api 以及 css 规范的不断改进,那些我们曾经认为实现起来很麻烦的功能也变得简单了起来。下面我们可以一起来探讨一下这些改进的...

    陈大鱼头
  • 使用 sroll-snap-type 优化滚动

    根据 CSS Scroll Snap Module Level 1 规范,CSS 新增了一批能够控制滚动的属性,让滚动能够在仅仅通过 CSS 的控制下,得到许多...

    Sb_Coco
  • 小程序开发基础-scroll-view 可滚动视图区域

    查看官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/

    达达前端
  • 微信小程序开发实战(1):容器组件

    本文主要介绍了滚动视图组件(scroll-view)的各种常用功能,例如,垂直和水平滚动、滚动事件等,并通过例子代码来演示这些功能的使用方法。

    蒙娜丽宁
  • uni-app中使用scroll-view滚到底部时多次触发scrolltolower

      前段时间使用scroll-view可滚动视图区域容器来做多个不同内容的展示(在我这个页面中同时使用了三个scroll-view做数据展示),因为这几个展示的...

    追逐时光
  • Mui获取页面滚动距离

    素描

扫码关注云+社区

领取腾讯云代金券