BetterScroll 在移动端滚动场景的应用


Posted in Javascript onSeptember 18, 2017

BetterScroll 是一款重点解决移动端各种滚动场景需求的开源插件( GitHub地址 ),适用于滚动列表、选择器、轮播图、索引列表、开屏引导等应用场景。

为了满足这些场景,它不仅支持惯性滚动、边界回弹、滚动条淡入淡出等效果的灵活配置,让滚动更加流畅,同时还提供了很多 API 方法和事件,以便我们更快地实现滚动场景下的需求,如下拉刷新、上拉加载。

由于它基于原生 JavaScript 实现,不依赖任何框架,所以既可以原生 JavaScript 引用,也可以与目前前端 MVVM 框架结合使用,比如,其官网上的示例就是与 Vue 的结合。

首先,让我们来看一下它是怎样让滚动更流畅的吧。

让滚动更流畅

在移动端,如果你使用过 overflow: scroll 生成一个滚动容器,会发现它的滚动是比较卡顿,呆滞的。为什么会出现这种情况呢?

因为我们早已习惯了目前的主流操作系统和浏览器视窗的滚动体验,比如滚动到边缘会有回弹,手指停止滑动以后还会按惯性继续滚动一会,手指快速滑动时页面也会快速滚动。而这种原生滚动容器却没有,就会让人感到卡顿。

BetterScroll 的滚动体验

试一试 BetterScroll 的滚动体验吧。体验地址

可以发现,在增加惯性滚动,边缘回弹等效果之后,明显流畅、舒服了很多。那么,这些效果是怎么实现的呢?

惯性滚动

BetterScroll 在用户滑动操作结束时,还会继续惯性滚动一段。首先看一下源码中的 BScroll.prototype._end 函数,这是 touchend、mouseup、touchcancel、mousecancel 事件的处理函数,也就是用户滚动操作结束时的逻辑。

BScroll.prototype._end = function (e) {
  ...
  if (this.options.momentum && duration < this.options.momentumLimitTime && (absDistY > this.options.momentumLimitDistance || absDistX > this.options.momentumLimitDistance)) {
   let momentumX = this.hasHorizontalScroll ? momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options)
    : {destination: newX, duration: 0}
   let momentumY = this.hasVerticalScroll ? momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options)
    : {destination: newY, duration: 0}
   newX = momentumX.destination
   newY = momentumY.destination
   time = Math.max(momentumX.duration, momentumY.duration)
   this.isInTransition = 1
  }
  ...
}

以上代码的作用是,在用户滑动操作结束时,如果需要开启了惯性滚动,用 momentum 函数计算惯性滚动距离和时间。该函数,根据用户滑动操作的速度和 deceleration选项 ——惯性减速来计算滚动距离,至于滚动时间,也是一个可配置的选项。

function momentum(current, start, time, lowerMargin, wrapperSize, options) { 
 ...
 let distance = current - start
 let speed = Math.abs(distance) / time
 ...
 let duration = swipeTime
 let destination = current + speed / deceleration * (distance < 0 ? -1 : 1)
 ...
}

边缘回弹

超过边缘时的回弹,有两个处理步骤,第一步是滚动到超过边界时速度要变慢,第二步是回弹到边界。其中,第一步是在源码的 BScroll.prototype._move 函数,这是 touchmove 和 mousemove 事件的处理函数,也就是在用户滑动操作过程中的逻辑。

// Slow down or stop if outside of the boundaries
if (newY > 0 || newY < this.maxScrollY) {
  if (this.options.bounce) {
    newY = this.y + deltaY / 3
  } else {
    newY = newY > 0 ? 0 : this.maxScrollY
  }
}

第二步是调用 BScroll.prototype.resetPosition 函数,回弹到边界。

BScroll.prototype.resetPosition = function (time = 0, easeing = ease.bounce) {
  ...
  let y = this.y
  if (!this.hasVerticalScroll || y > 0) {
   y = 0
  } else if (y < this.maxScrollY) {
   y = this.maxScrollY
  }
  ...
  this.scrollTo(x, y, time, easeing)
  ...
 }

流畅的滚动仅仅是基础,BetterScoll 真正的能力在于:提供了大量通用 / 定制的option 选项 、API 方法和事件,让各种滚动需求实现起来更高效。

如何应用于各种需求场景

下面,以结合 Vue 的使用为例,说一下 BetterScroll 在各种场景下的姿势。

普通滚动列表

比如,有如下列表:

{{item}}

我们想要让它垂直滚动,只需要对该容器进行简单的初始化。

import BScroll from 'better-scroll'
const options = {
 scrollY: true // 因为scrollY默认为true,其实可以省略
}
this.scroll = new BScroll(this.$refs.wrapper, options)

对于 Vue 中使用 BetterScroll,有一个需要注意的点是,因为在 Vue 模板中列表渲染还没完成时,是没有生成列表 DOM 元素的,所以需要在确保列表渲染完成以后,才能创建 BScroll 实例,因此在 Vue 中,初始化 BScroll 的最佳时机是 mouted 的 nextTick。

// 在 Vue 中,保证列表渲染完成时,初始化 BScroll
mounted() {
  setTimeout(() => {
   this.scroll = new BScroll(this.$refs.wrapper, options)
  }, 20)
},

初始化之后,这个 wrapper 容器就能够优雅地滚动了,并且可以通过 BScroll 实例 this.scroll 使用其提供的 API 方法和事件。

下面介绍几个常用的选项、方法和事件。

滚动条

scrollbar 选项,用来配置滚动条,默认为 false。当设置为 true 或者是一个 Object,开启滚动条。还可以通过 fade 属性,配置滚动条是随着滚动操作淡入淡出,还是一直显示。

// fade 默认为 true,滚动条淡入淡出
options.scrollbar = true
// 滚动条一直显示
options.scrollbar = {
 fade: false
}
this.scroll = new BScroll(this.$refs.wrapper, options)

具体效果可见普通滚动列表-示例。

下拉刷新

pullDownRefresh 选项,用来配置下拉刷新功能。当设置为 true 或者是一个 Object 的时候,开启下拉刷新,可以配置顶部下拉的距离(threshold)来决定刷新时机,以及回弹停留的距离(stop)

options.pullDownRefresh = {
 threshold: 50, // 当下拉到超过顶部 50px 时,触发 pullingDown 事件
 stop: 20 // 刷新数据的过程中,回弹停留在距离顶部还有 20px 的位置
}
this.scroll = new BScroll(this.$refs.wrapper, options)

监听 pullingDown 事件,刷新数据。并在刷新数据完成之后,调用 finishPullDown() 方法,回弹到顶部边界

this.scroll.on('pullingDown', () => {
 // 刷新数据的过程中,回弹停留在距离顶部还有20px的位置
 RefreshData()
  .then((newData) => {
   this.data = newData
   // 在刷新数据完成之后,调用 finishPullDown 方法,回弹到顶部
   this.scroll.finishPullDown()
 })
})

具体效果可见普通滚动列表-示例。

上拉加载

pullUpLoad 选项,用来配置上拉加载功能。当设置为 true 或者是一个 Object 的时候,可以开启上拉加载,可以配置离底部距离阈值(threshold)来决定开始加载的时机

options.pullUpLoad = {
 threshold: -20 // 在上拉到超过底部 20px 时,触发 pullingUp 事件
}
this.scroll = new BScroll(this.$refs.wrapper, options)

监听 pullingUp 事件,加载新数据。

this.scroll.on('pullingDown', () => {
 loadData()
  .then((newData) => {
   this.data.push(newData)
 })
})

具体效果可见普通滚动列表-示例。

选择器

wheel 选项,用于开启并配置选择器。可配置选择器当前选择的索引(selectedIndex),列表的弯曲弧度(rotate),以及切换选择项的调整时间(adjustTime)。

options.wheel = {
 selectedIndex: 0,
 rotate: 25,
 adjustTime: 400
}
// 初始化选择器的每一列
this.wheels[i] = new BScroll(wheelWrapper.children[i], options)

具体效果可见选择器 - 示例。

其中联动选择器,需要监听每个选择列表的选择,来改变其他选择列表。

data() {
  return {
   tempIndex: [0, 0, 0]
  }
},
...
// 监听每个选择列表的选择
this.wheels[i].on('scrollEnd', () => {
 this.tempIndex.splice(i, 1, this.wheels[i].getSelectedIndex())
})
...
// 根据当前选择项,确定其他选择列表的内容
computed: {
 linkageData() {
  const provinces = provinceList
  const cities = cityList[provinces[this.tempIndex[0]].value]
  const areas = areaList[cities[this.tempIndex[1]].value]

  return [provinces, cities, areas]
 }
},

具体效果可见选择器 - 示例中的联动选择器。

轮播图

snap 选项,用于开启并配置轮播图。可配置轮播图是否循环播放(loop),每页的宽度(stepX)和高度(stepY),切换阈值(threshold),以及切换速度(speed)。

options = {
 scrollX: true,
 snap: {
  loop: true, // 开启循环播放
  stepX: 200, // 每页宽度为 200px
  stepY: 100, // 每页高度为 100px
  threshold: 0.3, // 滚动距离超过宽度/高度的 30% 时切换图片
  speed: 400 // 切换动画时长 400ms
 }
}
this.slide = BScroll(this.$refs.slide, options)

具体效果可见轮播图 - 示例。

特殊场景

除了普通滚动列表、选择器、轮播图等基础滚动场景,还可以利用 BetterScroll 提供的能力,做一些特殊场景。

索引列表

索引列表,首先需要在滚动过程中实时监听滚动到哪个索引的区域了,来更新当前索引。在这种场景下,我们可以使用 probeType 选项,当此选项设置为 3 时,会在整个滚动过程中实时派发 scroll 事件。从而获取滚动过程中的位置。

options.probeType = 3
this.scroll = new BScroll(this.$refs.wrapper, options)
this.scroll.on('scroll', (pos) => {
 const y = pos.y
 for (let i = 0; i < listHeight.length - 1; i++) {
  let height1 = listHeight[i]
  let height2 = listHeight[i + 1]
  if (-y >= height1 && -y < height2) {
   this.currentIndex = i
  }
 }
})

当点击索引时,使用 scrollToElement()方法 滚动到该索引区域。

scrollTo(index) {
 this.$refs.indexList.scrollToElement(this.$refs.listGroup[index], 0)
}

具体效果可见索引列表 - 示例。

开屏引导

开屏引导,其实就是一种不自动循环播放的横向滚动轮播图而已。

options = {
 scrollX: true,
 snap: {
  loop: false
 }
}
this.slide = BScroll(this.$refs.slide, options)

具体效果可见开屏引导 - 示例。因为此需求场景一般只有移动端才有,所以最好在手机模式下看效果。

自由滚动

freeScroll 选项,用于开启自由滚动,允许横向和纵向同时滚动,而不限制在某个方向。

options.freeScroll = true

另外需要注意的是,此选项在eventPassthrough 设置了保持原生滚动时无效。

具体效果可见自由滚动-示例。

小结

BetterScroll 可以用于几乎所有滚动场景,本文仅介绍了在一些典型场景下的使用姿势。

作为一款旨在解决移动端滚动需求的插件,BetterScroll 开放的众多选项、方法和事件,其实,就是提供了一种让我们更加快捷、灵活、精准时机地处理滚动的能力。

以上所述是小编给大家介绍的BetterScroll 在移动端滚动场景的应用,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Javascript SHA-1:Secure Hash Algorithm
Dec 20 Javascript
jquery.ui.draggable中文文档
Nov 24 Javascript
js 变量类型转换常用函数与代码[比较全]
Dec 01 Javascript
js 关于=+与+=日期函数使用说明(赋值运算符)
Nov 15 Javascript
热点新闻滚动特效的js代码
Aug 17 Javascript
JQuery中使文本框获得焦点的方法实例分析
Feb 28 Javascript
JavaScript、jQuery与Ajax的关系
Jan 24 Javascript
Javascript 调用 ActionScript 的简单方法
Sep 22 Javascript
vuejs通过filterBy、orderBy实现搜索筛选、降序排序数据
Oct 26 Javascript
vue2.0使用swiper组件实现轮播效果
Nov 27 Javascript
vue 录制视频并压缩视频文件的方法
Jul 27 Javascript
vue监听用户输入和点击功能
Sep 27 Javascript
Windows下Node.js安装及环境配置方法
Sep 18 #Javascript
jQuery选择器之子元素选择器详解
Sep 18 #jQuery
JavaScript实现的斑马线表格效果【隔行变色】
Sep 18 #Javascript
js实现可以点击收缩或张开的悬浮窗
Sep 18 #Javascript
javascript流程控制语句集合
Sep 18 #Javascript
20170918 前端开发周报之JS前端开发必看
Sep 18 #Javascript
Angular中的$watch方法详解
Sep 18 #Javascript
You might like
自制短波长线天线频率预选器 - 成功消除B2K之流的镜像
2021/03/02 无线电
阿拉伯的咖啡与水烟
2021/03/03 咖啡文化
php xml实例 留言本
2009/03/20 PHP
phalcon model在插入或更新时会自动验证非空字段的解决办法
2016/12/29 PHP
刷新页面的几种方法小结(JS,ASP.NET)
2014/01/07 Javascript
js点击事件链接的问题解决
2014/04/25 Javascript
仿百度联盟对联广告实现代码
2014/08/30 Javascript
javascript获取flash版本号的方法
2014/11/20 Javascript
jQuery Mobile框架中的表单组件基础使用教程
2016/05/17 Javascript
js实现碰撞检测特效代码分享
2016/10/16 Javascript
Vue2.x中的Render函数详解
2017/05/30 Javascript
Vue.js中extend选项和delimiters选项的比较
2017/07/17 Javascript
详解基于vue-cli配置移动端自适应
2018/01/13 Javascript
JS实现可视化文件上传
2018/09/08 Javascript
javascript数据类型中的一些小知识点(推荐)
2019/04/18 Javascript
js Math数学简单使用操作示例
2020/03/13 Javascript
Vue中父子组件的值传递与方法传递
2020/09/28 Javascript
python实现简易内存监控
2018/06/21 Python
解决pycharm的Python console不能调试当前程序的问题
2019/01/20 Python
Django框架实现的分页demo示例
2019/05/25 Python
用Python画小女孩放风筝的示例
2019/11/23 Python
python获取栅格点和面值的实现
2020/03/10 Python
完美解决python针对hdfs上传和下载的问题
2020/06/05 Python
Python计算信息熵实例
2020/06/18 Python
Django中Q查询及Q()对象 F查询及F()对象用法
2020/07/09 Python
Python实现简单的2048小游戏
2021/03/01 Python
HTML5手机端弹出遮罩菜单特效代码
2016/01/27 HTML / CSS
The Hut美国/加拿大:英国领先的豪华在线百货商店
2019/03/26 全球购物
电气自动化大学生求职信
2013/10/16 职场文书
化工操作工岗位职责
2014/04/29 职场文书
开业庆典活动策划方案
2014/09/21 职场文书
2014年体育教师工作总结
2014/12/03 职场文书
本溪水洞导游词
2015/02/11 职场文书
导游词之苏州寒山寺
2019/12/05 职场文书
MySQL系列之十二 备份与恢复
2021/07/02 MySQL
Java死锁的排查
2022/05/11 Java/Android