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 相关文章推荐
使用IE的地址栏来辅助调试Web页脚本
Mar 08 Javascript
Jquery实现控件的隐藏和显示实例
Feb 08 Javascript
select多选 multiple的使用示例
Jun 16 Javascript
Node.js实现文件上传
Jul 05 Javascript
JS碰撞运动实现方法详解
Dec 15 Javascript
JavaScript正则获取地址栏中参数的方法
Mar 02 Javascript
前端页面文件拖拽上传模块js代码示例
May 19 Javascript
Vue 中 template 有且只能一个 root的原因解析(源码分析)
Apr 11 Javascript
解决Antd Table组件表头不对齐的问题
Oct 27 Javascript
Antd表格滚动 宽度自适应 不换行的实例
Oct 27 Javascript
arcgis.js控制地图地体的显示范围超出区域自动弹回(实现思路)
Jan 28 Javascript
javascript的var与let,const之间的区别详解
Feb 18 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
咖啡语言
2021/03/03 咖啡文化
php5 and xml示例
2006/11/22 PHP
CodeIgniter钩子用法实例详解
2016/01/20 PHP
Javascript 圆角div的实现代码
2009/10/15 Javascript
jquery 读取页面load get post ajax 四种方式代码写法
2011/04/02 Javascript
javascript函数以及基础写法100多条实用整理
2013/01/13 Javascript
javascript对下拉列表框(select)的操作实例讲解
2013/11/29 Javascript
一个判断抢购时间是否到达的简单的js函数
2014/06/23 Javascript
jQuery实现列表的全选功能
2015/03/18 Javascript
jQuery 弹出层插件(推荐)
2016/05/24 Javascript
详解JavaScript的闭包、IIFE、apply、函数与对象
2016/12/21 Javascript
jquery表单验证插件validation使用方法详解
2017/01/20 Javascript
vue拦截器Vue.http.interceptors.push使用详解
2017/04/22 Javascript
关于页面刷新vuex数据消失问题解决方案
2017/07/03 Javascript
node.js操作MongoDB的实例详解
2017/10/11 Javascript
JavaScript中变量提升与函数提升经典实例分析
2018/07/26 Javascript
看看“疫苗查询”小程序有温度的代码
2018/07/31 Javascript
vue-cli 默认路由再子路由选中下的选中状态问题及解决代码
2018/09/06 Javascript
vue动态绘制四分之三圆环图效果
2019/09/03 Javascript
JS函数本身的作用域实例分析
2020/03/16 Javascript
Javascript异步编程async实现过程详解
2020/04/02 Javascript
python服务器与android客户端socket通信实例
2014/11/12 Python
Python 爬虫学习笔记之正则表达式
2016/09/21 Python
Python 基础之字符串string详解及实例
2017/04/01 Python
python中dir()与__dict__属性的区别浅析
2018/12/10 Python
python获取本机所有IP地址的方法
2018/12/26 Python
详解Python+Selenium+ChromeDriver的配置和问题解决
2021/01/19 Python
Python Selenium异常处理的实例分析
2021/02/28 Python
伦敦所有西区剧院演出官方票务代理:Theatre Tickets Direct
2017/05/26 全球购物
嘻哈珠宝品牌:KRKC&CO
2020/10/19 全球购物
开学典礼主持词
2014/03/19 职场文书
2014基层党员批评与自我批评范文
2014/09/24 职场文书
办护照工作证明
2014/10/01 职场文书
先进人物事迹材料
2014/12/29 职场文书
消费者投诉书范文
2015/07/02 职场文书
公司董事任命书
2015/09/21 职场文书