vue中利用iscroll.js解决pc端滚动问题


Posted in Javascript onFebruary 15, 2020

项目中经常遇到区域超出部分会出现滚动条,滚动条在pc端可以通过鼠标滚轮控制上下,在移动端可以通过鼠标拖动页面进行滚动,这两种场景都是符合用户习惯,然而这种滚动条一般都是竖【vertical】项滚动条,如果pc端出现横向滚动条【horizontal】,在不做处理的情况下,你只能用鼠标拖动横向滚动条按钮【scrollerbar】展示滚动区域,而且为了美观,一般滚动条会进行样式编写或者隐藏,那么横向区域默认情况下就没法滚动。

二、描述

现为了解决pc端滚动区域能像移动端一样,能够通过鼠标拖动滚动区域直接进行滚动,如图所示

vue中利用iscroll.js解决pc端滚动问题

pc端滚动示例图

滚动实例用到知识点如下:

  1. 采用vue-cli3+iscroll.js组合的方式;
  2. 使用 vue 自定义指令实现 iscroll 实例化和参数配置;
  3. 实现横向滚动区域和竖向滚动区域之间的联动;
  4. 实现横向滚动条居中显示和使用scrollIntoView()方法的差别

三、自定义指令 v-iscroll

1、新建指令文件

这里使用 vue 自定义指令初始化 iscroll 实例,在 vue-cli3 项目目录下新建vIscroll.js,文件代码如下:

const IScroll = require('iscroll')
const VIScroll = {
 install: function (Vue, options) {
 Vue.directive('iscroll', {
 inserted: function (el, binding, vnode) {
 let callBack
 let iscrollOptions = options
 <!--vue组件中绑定的两个参数 option、instance-->
 const option = binding.value && binding.value.option
 const func = binding.value && binding.value.instance
 // 判断输入参数
 const optionType = option ? [].toString.call(option) : undefined
 const funcType = func ? [].toString.call(func) : undefined
 // 兼容 google 浏览器拖动
 el.addEventListener('touchmove', function (e) {
  e.preventDefault()
 })
 // 将参数配置到new IScroll(el, iscrollOptions)中
 if (optionType === '[object Object]') {
  iscrollOptions = option
 }
 if (funcType === '[object Function]') {
  callBack = func
 }
 // 使用vnode绑定iscroll是为了让iscroll对象能够夸状态传递,避免iscroll重复建立
 // 这里面跟官方网站 const myScroll = new IScroll('#wrapper',option) 初始化一样
 vnode.scroll = new IScroll(el, iscrollOptions)
 // 如果指令传递函数进来,把iscroll实例传递出去
 if (callBack) callBack(vnode.scroll)
 },
 componentUpdated: function (el, binding, vnode, oldVnode) {
 // 将scroll绑定到新的vnode上,避免多次绑定
 vnode.scroll = oldVnode.scroll
 // 使用 settimeout 让refresh跳到事件流结尾,保证refresh时数据已经更新完毕
 setTimeout(() => {
  vnode.scroll.refresh()
 }, 0)
 },
 unbind: function (el, binding, vnode, oldVnode) {
 // 解除绑定时要把iscroll销毁
 vnode.scroll = oldVnode.scroll
 vnode.scroll.destroy()
 vnode.scroll = null
 }
 })
 }
}
module.exports = VIScroll

这里附上 iscroll.js 5 官方文档地址, iscroll npm 包地址,相关属性和方法自行查看。

2、加载引用指令

首先在 main.js 中加载指令:

import Vue from 'vue'
import App from './App.vue'
import "./assets/reset.css"
// 加载scroll指令
import VIscroll from './directive/vIscroll'
Vue.use(VIscroll)
Vue.config.productionTip = false

new Vue({
 render: h => h(App),
}).$mount('#app')

使用指令,摘自 tabList.vue 组件部分代码如下:

<template>
 <div class="tab-container">
 <div
  class="scroll-container"
  v-iscroll="{
  option: iscrollConf,
  instance: getIscroll
  }"
  ref="scrollContainer"
 >
  <ul
  class="tab-li-container"
  ref="tabLiContainer"
  >
  <li
   class="tab-li-item"
   v-for="(item, index) in list"
   :key="item.id"
   :id="item.id"
   ref="tabItem"
   @click="tabEvent(item, index)"
  >
   <div
   class="item"
   :class="{
    'item-active': currentId == item.id
   }"
   >{{item.num}}</div>
  </li>
  </ul>
 </div>
 <div
  class="tab-left"
  @click="tabBtnEvent('left')"
 ><</div>
 <div
  class="tab-right"
  @click="tabBtnEvent('right')"
 >></div>
 </div>
</template>
<script>
export default {
 props: ['list'],
 data () {
 return {
  iscrollConf: {
  bounce: true,
  mouseWheel: true,
  click: true,
  scrollX: true,
  scrollY: false
  },
  currentId: null,
  currentIndex: 0,
  myScroll: null
 }
 },
 mounted () {
 this.$refs.tabLiContainer.style.width = this.$refs.tabItem[0].offsetWidth * this.list.length + 'px'
 this.$nextTick(() => {
  this.myScroll.refresh()
 })
 },
 methods: {
 tabEvent (item, currentIndex) {
  <!--点击某个li 按钮事件处理逻辑-->
 },
 tabBtnEvent (direction) {
  <!--左右切换逻辑事件-->
 },
 getIscroll (iscroll) {
  this.myScroll = iscroll
 }
 },
 watch: {
 list: {
  handler (l) {
  this.currentId = l[0].id
  },
  immediate: true,
  deep: true
 }
 }
}
</script>
<style scoped>
// 样式
</style>

上述代码中 v-iscroll 指令传入两个字段参数:

option:配置iscroll参数,这里面注意scrollX,scrollY两个属性,代表的是横向还是竖向滚动;
instance:回调方法的调用, vIscroll.js 中执行回调方法,通过该组件方法 getIscroll() 获取到 iscroll 的实例。

3、上下滚动区域联动

上面的代码可以解决开篇场景中的问题,现在实现上下区域联动,通过点击横向滚动条某个按钮,使其变成选中状态,然后竖向滚动条对应的项跳到首位,如图所以:

vue中利用iscroll.js解决pc端滚动问题

联动示例图

3-1、联动实现方法

点击按钮的方法:

tabEvent (item, currentIndex) {
 this.currentId = item.id
 this.currentIndex = currentIndex
 <!--这里实现按钮始终居中显示,暂时省略,下面补充-->
 ...
 <!--传给竖向滚动组件-->
 this.$emit("switchTab", this.currentId, this.currentIndex)
},

竖向滚动区域组件【App.vue】代码部分如下,并对 switchTab() 方法进行详细注释:

<template>
 <div id="app">
 <TabList
  :list="list"
  @switchTab="switchTab"
 ></TabList>
 <!-- v-iscroll="defalutOption" -->
 <div
  v-iscroll="{
  option: defalutOption,
  instance: getIscroll
  }"
  class="tab-content-container"
  ref="detailItemContainer"
 >
  <ul class="tab-list-container">
  <li
   v-for="item in list"
   :key="item.id"
   class="list-item"
   ref="detailItem"
  >
   <div>{{item.value}}</div>
  </li>
  </ul>
 </div>
 </div>
</template>

<script>
import TabList from './components/tabList.vue'

export default {
 name: 'App',
 components: {
 TabList,
 },
 data () {
 return {
  list: [
  { id: 1, value: '这是第1题', num: 1 },
  <!--...省略数据展示-->
  { id: 16, value: '这是第16题', num: 16 }
  ],
  defalutOption: {
  bounce: true,
  mouseWheel: true,
  click: true,
  scrollX: false,
  scrollY: true
  },
  myScroll: null
 }
 },
 methods: {
 switchTab (currentId, currentIndex) {
  <!--对选中的当前项,这里就是“3”按钮对应的“这是第3题”,求出它距离父元素的上边距offsetTop值-->
  const offsetTop = this.$refs.detailItem[currentIndex].offsetTop
  <!--滚动的范围不能超过这个滚动体的底部,这里面用到iscroll的属性maxScrollY-->
  const y = offsetTop >= Math.abs(this.myScroll.maxScrollY) ? this.myScroll.maxScrollY : -offsetTop
  <!--调用iscroll的方法进行滚动到相应的位置-->
  this.myScroll.scrollTo(0, y)
 },
 <!--获取实例-->
 getIscroll (iscroll) {
  this.myScroll = iscroll
 }
 }
}
</script>
<style scoped>
<!--样式-->
...
</style>

这里面用到的都是 iscroll 插件自带的属性和方法进行滚动边界的判断和滚动,比用 JavaScript 方法方便的多,而且用了iscroll作为滚动容器,已经在vIscroll.js禁用了相关浏览器默认事件。

3-2、居中显示

这里 JavaScript 有个 scrollIntoView() 方法, 官方文档链接 ,这个方法让当前的元素滚动到浏览器窗口的可视区域内。关键缺点是,如果横向滚动和竖向滚动都同时用到这个方法,只能保证一个滚动区域有效,另一个会不滚动。

使用 scrollIntoView() 方法配置如下:

this.$refs.tabItem[this.currentIndex].scrollIntoView({
 behavior: "smooth",
 inline: "center",
 block: 'nearest'
})

这里在横向滚动区域添加了一对左右按钮,实现切换功能,如图所示:

vue中利用iscroll.js解决pc端滚动问题

切换按钮示例图

切换按钮事件方法就是通过改变上一个、下一个按钮下标,调用方法,实现切换功能,切换事件方法逻辑如下:

tabBtnEvent (direction) {
 const max = this.$refs.tabItem.length
 if (direction === 'left' && this.currentIndex > 0) {
 this.currentIndex--
 }
 if (direction === 'right' && this.currentIndex < max - 1) {
 this.currentIndex++
 }
 <!--调用单击按钮事件-->
 this.tabEvent(this.$refs.tabItem[this.currentIndex], this.currentIndex)
},

下面对 单击按钮事件 添加居中逻辑,详细代码和解析图如下,可以对比查看:

vue中利用iscroll.js解决pc端滚动问题

居中计算图

tabEvent (item, currentIndex) {
 this.currentId = item.id
 this.currentIndex = currentIndex
 // 获取滚动容器的长度的一半,即中间点
 const scrollContainerHalfWidth = this.$refs.scrollContainer.offsetWidth / 2
 // 获取单个item的一半长度
 const tabItemHalfWidth = this.$refs.tabItem[currentIndex].offsetWidth / 2
 // 求取插值,就是开始到中间开始位置的距离
 const halfDistance = scrollContainerHalfWidth - tabItemHalfWidth
 // 求取当前item的相对总长度的偏移量
 const currentItemOffsetLeft = this.$refs.tabItem[currentIndex].offsetLeft
 // scroll 移动到中间的值
 const x = halfDistance - currentItemOffsetLeft
 this.myScroll.scrollTo(x, 0)
 this.$emit("switchTab", this.currentId, this.currentIndex)
},

4、总结

1、整个实例用的都是iscroll插件相关属性实现的滚动,避免同时使用JavaScript方法造成的代码混乱;

2、利用自定义指令的方式有效的避免了传统实例化iscroll带来的代码冗余,使其方便简洁;

3、本实例滚动选项都是字符串,如果出现图片的情况,合理使用iscroll.refresh() 方法,在正确的时期重新计算滚动区域,避免滚动边界受限;

总结

以上所述是小编给大家介绍的vue中利用iscroll.js解决pc端滚动问题,希望对大家有所帮助!

Javascript 相关文章推荐
Jquery Autocomplete 结合asp.net使用要点
Oct 29 Javascript
jquery实现的可隐藏重现的靠边悬浮层实例代码
May 27 Javascript
JS执行删除前的判断代码
Feb 18 Javascript
jQuery中ajax的load()与post()方法实例详解
Jan 05 Javascript
Node调用Java的示例代码
Sep 20 Javascript
angular4中引入echarts的方法示例
Jan 29 Javascript
vue中格式化时间过滤器代码实例
Apr 17 Javascript
vue项目中将element-ui table表格写成组件的实现代码
Jun 12 Javascript
vue-cli配置flexible过程详解
Jul 04 Javascript
vue计算属性无法监听到数组内部变化的解决方案
Nov 06 Javascript
微信小程序语音同步智能识别的实现案例代码解析
May 29 Javascript
微信小程序向Java后台传输参数的方法实现
Dec 10 Javascript
JS localStorage存储对象,sessionStorage存储数组对象操作示例
Feb 15 #Javascript
ES6 proxy和reflect的使用方法与应用实例分析
Feb 15 #Javascript
js表达式与运算符简单操作示例
Feb 15 #Javascript
ES6 class类链式继承,实例化及react super(props)原理详解
Feb 15 #Javascript
ES6 Iterator遍历器原理,应用场景及相关常用知识拓展详解
Feb 15 #Javascript
es6 super关键字的理解与应用实例分析
Feb 15 #Javascript
es6中class类静态方法,静态属性,实例属性,实例方法的理解与应用分析
Feb 15 #Javascript
You might like
用php将任何格式视频转为flv的代码
2009/09/03 PHP
php获取参数的几种方法总结
2014/02/18 PHP
PHP树的深度编历生成迷宫及A*自动寻路算法实例分析
2015/03/10 PHP
PHP中常见的密码处理方式和建议总结
2018/10/14 PHP
PHP Pipeline 实现中间件的示例代码
2020/04/26 PHP
只需20行代码就可以写出CSS覆盖率测试脚本
2013/04/24 Javascript
ExtJS4利根据登录后不同的角色分配不同的树形菜单
2014/05/02 Javascript
js获取IP地址的方法小结
2014/07/01 Javascript
jQuery实现checkbox全选的方法
2015/06/10 Javascript
JS模式之单例模式基本用法
2015/06/30 Javascript
Bootstrap安装环境配置教程分享
2016/05/27 Javascript
表单input项使用label同时引用Bootstrap库导致input点击效果区增大问题
2016/10/11 Javascript
微信小程序 高德地图SDK详解及简单实例(源码下载)
2017/01/11 Javascript
JavaScript实现计算圆周率到小数点后100位的方法示例
2018/05/08 Javascript
JQuery实现ajax请求的示例和注意事项
2018/12/10 jQuery
30分钟用Node.js构建一个API服务器的步骤详解
2019/05/24 Javascript
Vue指令之 v-cloak、v-text、v-html实例详解
2019/08/08 Javascript
python+pyqt实现12306图片验证效果
2017/10/25 Python
Python使用wxPython实现计算器
2018/01/30 Python
python numpy 常用随机数的产生方法的实现
2019/08/21 Python
python3读取autocad图形文件.py实例
2020/06/05 Python
小饰品店的创业计划书范文
2013/12/28 职场文书
跟单文员岗位职责
2014/01/03 职场文书
四风存在的原因分析
2014/02/11 职场文书
保护环境演讲稿
2014/05/10 职场文书
食品流通安全承诺书
2014/05/22 职场文书
小学绿色学校申报材料
2014/08/23 职场文书
珍惜资源的建议书
2014/08/26 职场文书
2014年最新学校运动会广播稿
2014/09/17 职场文书
四风问题个人对照检查剖析材料
2014/09/27 职场文书
2014年政协委员工作总结
2014/12/01 职场文书
2015年幼师个人工作总结
2015/10/15 职场文书
优秀共产党员主要事迹材料
2015/11/05 职场文书
《雪地里的小画家》教学反思
2016/02/16 职场文书
毕业季聚会祝酒词!
2019/07/04 职场文书
sql注入教程之类型以及提交注入
2021/08/02 MySQL