Bootstrap

2.8 scroll-view介绍:在小程序中如何实现滚动锚定?(一)

视频链接:

2.8 scroll-view 介绍:在小程序中如何实现滚动锚定?(一)

片 1

 

上节课我们介绍了 moveable-view 与 movable-area 这两个组件,并且用它们实现了侧滑删除功能,还学习了元素如何定位,如何在小程序中使用 npm的方式安装模块,如何以扩展声明的方式使用 weui 组件库等等,这节课我们开始学习 scroll-view 组件,这是一个可滚动的视图区域容器组件。

 

不知道你有没有这样的阅读体验,在阅读一个页面时,页面上从上到下有很多图片,当我们正在查看下面的某一张图片时,突然上边有一张图片加载出来,把我们正在看的图片给挤跑了。

 

这是一个体验问题。

 

Google 为了改进用户的滚动阅读体验,针对这个问题推出滚动锚定解决方案。这节课我们就看一看,在小程序中如何使用滚动锚定这个功能。

 

首先我们先看看 scroll-view 这个组件。

 

在一个框架内,每个组件的设计,都有设计者的考虑。如果说 view 的存在,是为了实现各种常见的 ui 布局,那么今天我们学习的 scroll-view 组件,则是为了特定的滚动场景设计的。它与 movable-view、movable-area、cover-view 等组件一样,都是为了方便开发者,实现特定场景下的业务功能而设计的。

 

没有这些组件,开发者自己通过 view 也能实现同样的功能。但有了这些组件,实现起来更简单了。

 

片 2

 

除了常规的竖向滚动,在横向滚动上,可滚动容器有一个经典的应用场景。在购物 App 上,我们经常看到这样的功能,首部导航按钮可以横向滚动(见上方示意图)。因为导航按钮太多了,产品经理将不经常用的按钮,放在了第二屏,用户需要向左滑动一下,才可以看到后面的按钮。

 

在这个地方,有一个实际内容宽度大于手机屏幕宽度的子容器,它支持用户,用手指左右滑动。下方还有一个滑动指示条,这是根据滚动位置计算出来的。

 

了解 scroll-view 的滚动属性

片 3

 

scroll-view 是一个略显复杂的组件。它的属性主要实现了两套功能:一,左右或上下滚动;二,下拉更新。

 

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

片 4

 

我们先看看这些与滚动有关的属性。其中,scroll-x、scroll-y 这两个属性,默认都是 false,也就是说默认情况下,在两个方向上都不开启滚动。

 

当 scroll-view 的scroll-y 属性为真时,允许纵向滚动;当添加 scroll-x 属性时,允许横向滚动。

 

片 5

 

在效果图中 我们可以看到(见上方效果图 1),上面第1个启用的是 scroll-x,下面第2个启用的是 scroll-y,第3个是两个属性都启用了。由于手机屏幕比较窄,横向滚动需求是比较常见的。

 

从实践结果看,scroll-x 与 scroll-y 不是一对互斥的属性,并不是说,我们设置了 scroll-y,就不能设置 scroll-x了。

 

两个方向的滚动可以同时开启,但是在操作的时候,只能同时朝一个方向滚动(见上方效果图 2)。两个方向都开启时,通过事件函数的打印可以看到,scroll-top 与 scroll-left 都是有值的。

 

(可以从本课视频中查看示例运行效果)

 

如何理解 scroll-top 属性?

片 6

 

接下来我们看一下,我们应该如何理解 scroll-top、scroll-left 这两个属性呢?

 

片 7

 

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

 

同理,scroll-left 是类似的。当开启的是横向滚动时,scroll-left 指滚动实体的左边,距离父容器盒子的左边界的距离(见上面示意图,红线是 scroll-left)。

 

scroll-top、scroll-left 都是大小等于 0 的,不会出现负值。

 

我们一般说“滚动到顶部、滚动到底部”,在这里与属性的定义是相反的。“滚动到顶部与滚动到底部”,指的不是内部滚动实体,滚动到了它所能达到的最大值、最小值,而是指滚动实体顶部边缘,到达了距离滚动外框最大距离的外部,在这里都是以父容器,也就是滚动外框边缘为参照物的。

 

此外呢,还有一点我们需要注意,像 scroll-top、scroll-left 这两个属性,它们都是可以通过属性绑定、控制组件行为的属性。在上节课中,我们学习的 movable-view,它的 x、y 也是这样的属性。

 

如果我们想让内部的滚动实体滚动到某个位置,并不能直接去调用它的,一个类似于 scrollTo 的方法。我们只能在 JS 里动态改变 scroll-top、scroll-left 这两个属性绑定的变量,然后视图渲染以后,组件会自动发生滚动。

 

关于绑定更新

片 8

 

在 vue 和小程序的页面中,到处都是这样的响应式绑定机制,不是直接去调用页面上组件的方法,而只是给组件的属性绑定一个值,然后等待组件自己更新。

 

在面向对象的软件设计中,一般我们为一个对象定义一个类,这个类既有方法,又有属性。在使用时,我们将这个类实例化,既可以改变实例的属性,又可以调用实例的方法;并且在大多数情况下,我们改变属性时,并不会让实例发生任何行为,而只有显式调用它的方法时,它才会有动作发生。

 

现在,在前端这一块,像 vue、小程序这样的框架,把这个传统给颠覆了。直接传一个值,让组件自己负责更新,这样看起来更简单。但是在复杂的业务逻辑中,如果直接能调用组件的方法,可能会更简单一些,因为那样连用于属性绑定的变量,都不需要事先声明了。有时候这种声明是有点冗余的。

 

scroll-into-view 属性

片 9

 

我们接着看scroll-view与滚动有关的属性,与 scroll-top、scroll-left 类似的属性,还有 scroll-into-view,它用于滚动到某个元素。这个属性很好理解,它的值必须是一个scroll-view 的子组件的 id。当滚动时,小程序是以子组件的上、左边界为测算依据的。也就是说,纵向滚动,使scroll -view 的 scroll-top 等于子组件的上边界;横向滚动,使 scroll -view 的 scroll-left 等于子组件的左边界。

 

实际上这是一个语法糖属性。

 

它帮助开发者做了一些事情。没有这个属性,我们通过 id 查找组件,找到子组件的上边、左边,计算出它们距离滚动边框上边缘、左边缘的距离,通过设置 scroll-top、scroll-left 这两个属性,也可以达到同样的目的。

 

官方文档里讲,在使用 scroll-into-view 时,“设置哪个方向可滚动,则在哪个方向滚动到该元素”。

 

这里有一个问题:前面我们知道了,scroll-x、scroll-y 这两个布尔属性并不互斥,假如我们同时开启横向、纵向两个方向的滚动,当通过 scroll-into-view 滚动时,那么它的滚动行为是怎么样的呢?

 

是先向 x 方向滚动,还是先向 y 方向滚动?还是两个方向同时滚动?

 

通过测试结果来看,结论很不明朗,如果不加 scroll-with-animation这个属性的话,也就是不开启动画,可以同时在 x、y 两个方向上瞬时移动到目标位置;如果开启了动画,同一时间就只能在一个方向上滚动了,有时在 x 方向上滚动,有时在 y 方向上滚动,行为很不明确。

 

所以我们不妨这样下结论:scroll-x 与 scroll-y 最好不要同时开启。如果功能上有这个需求,可以自己基于 view 实现同样的功能,或者简单的方法是,先在 x 方向上开启,完成移动以后,再在 y 方向上开启,依次进行,这样就没问题了。

 

(可以从本课视频中查看运行效果)

 

从这个测试事例来看,程序都是程序员编写出来的,功能也都是有边界的,没有编写过那部分代码,自然也不可能有那部分代码的功能。在使用时,我们要避免这种情况,当然在设计框架时,我们也需要考虑避免这种情况,尽量减少产生不明确行为的可能。

 

滚动锚定:scroll-anchoring

片 10

 

这个属性 scroll-anchoring,非常值得一提,它涉及我们这节课开始时提出的问题。这个属性默认是 false,添加后,功能才会开启。它是控制「滚动锚定」特征的,也就是控制滚动位置不随内容变化而抖动的。

 

那么,什么是滚动锚定呢?

 

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

 

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

 

这个体验肯定很不好。这种情况据统计,在用户浏览行为中占比达到 1%。

 

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

 

scroll-view 的 scroll-anchoring 这个属性,就是干这个用的。它是一个布尔属性,添加它以后,当上面的内容扩充时,小程序页面会自动向上滚动一段距离。

 

这就是「滚动锚定」策略。不是没有滚动,而是滚动自动抵消了,如果我们监听前后 scroll-top 的值,它们已经不一样了。

 

但是这个属性,在某种情况下,可能会给开发者带来意想不到的 bug。这是一个什么问题呢?如果你是一位vue 开发者,可能也遇到过这样的问题。

 

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

 

overflow-anchor: none


overflow-anchor: auto

片 11

 

当出现这种现象时,简单的解决方法,就是关闭「滚动锚定」策略,或设置一个具有相同效果的样式,overflow-anchor 等于 none(见上方代码)。

 

同理,开启这个策略,也可以通过这个样式开启。scroll-anchoring 这个属性,目前小程序只支持 iOS 手机,在 Android 手机上需要开发者自己处理。那么,在 Android 手机上可以添加这样的样式,overflow-anchor 等于 auto(见上方代码)。

 

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

片 12

 

接下来我们接着看关于下拉更新的属性,upper-threshold、lower-threshold 这两个属性,是用于控制 scrolltoupper 和 scrolltolower 事件何时派发的,默认都是 50px。

 

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

 

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

 

并且这种派发是随心所欲的。并不是 scroll 派发一次,scrolltoupper 就只派发一次,派发基本是毫无规律的。 所以,在基于 scrolltoupper、scrolltolower 这两件事情写业务逻辑时,我们要特别注意判断,是否已经处理过了,以免造成重复处理。

 

(可以从本课视频中查看示例运行效果)

 

既然 upper-threshold 代表了距离顶部或左边多远,lower-threshold 代表了距离底部或右边多远,它俩都是以一抵二的属性,但是 scroll-top、scroll-left,还有 scroll-x、scroll-y 都是分成两拨的,学习时需要注意区别一下。

 

在 flex 布局里,我们知道当 flex-direction 样式为 row 或 column 时,样式值 flex-start、flex-end 分别也代表了不同的含义。这种思维更符合程序员的逻辑。

 

如果 scroll-top、scroll-left,可以合并为 scroll-start,可能这样更好理解一些。像 scroll-x、scroll-y 本来也是一对互斥的属性,可以合并为一个 scroll-direction 属性,值可以取 horizontal 或 vertical,分别代表开启横向滚动或竖向滚动。这两个值在 movable-view 中已经使用过了,使用相同含义的名称,有助于减少记忆负担。

 

课后作业

 

上节课我们留了一个作业:在使用 mp-slideview 组件时,当在内部复用 mp-cell 组件时,主要的内容文字没有上下居中显示。在引用 weui 组件时,如何在组件外面控制其内部的样式表现呢?怎么控制示例中的文本,让它居中显示呢?

 

所有的weui组件,包括mp-slideview,都有一个ext-class属性,这个属性是添加在组件内部结构上的class类样式,可用于修改weui组件内部的样式。

 

.slideViewClass .weui-cell{

padding: 0;

}

片13

 

示例中内部mp-cell的样式名为.weui-cell,我们只需要在wxss文件中,定义一个这样的样式(见上面样式代码),就可以实现重写内部组件样式了。

 

好,今天的课就讲到这里。这节课主要介绍了 scroll-view 组件关于滚动的相关属性,了解了滚动锚定等内容,下节课我们继续学习 scroll-view 组件,着手自定义实现一个下拉刷新功能。

 

2020 年 5 月 22 日