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打造支持汉字,拼音,英文快速定位查询的超级select插件
Jun 18 Javascript
JavaScript中split() 使用方法汇总
Apr 17 Javascript
JavaScript实现打字效果的方法
Jul 10 Javascript
浅谈jQuery animate easing的具体使用方法(推荐)
Jun 17 Javascript
对js eval()函数的一些见解
Aug 15 Javascript
vue.js中$watch的用法示例
Oct 04 Javascript
jQuery实现的手风琴侧边菜单效果
Mar 29 jQuery
Vue.js实战之Vuex的入门教程
Apr 01 Javascript
ES6解构赋值实例详解
Oct 31 Javascript
浅谈webpack4 图片处理汇总
Sep 12 Javascript
javascript实现导航栏分页效果
Jun 27 Javascript
使用 Angular RouteReuseStrategy 缓存(路由)组件的实例代码
Nov 01 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
锁定年轻人的双倍活力 星巴克推出星倍醇即饮浓咖啡
2021/03/03 咖啡文化
php urlencode()与urldecode()函数字符编码原理详解
2011/12/06 PHP
php eval函数用法总结
2012/10/31 PHP
php基于Snoopy解析网页html的方法
2015/07/09 PHP
php代码调试利器firephp安装与使用方法分析
2018/08/21 PHP
PHP实现的权重算法示例【可用于游戏根据权限来随机物品】
2019/02/15 PHP
JQuery调webservice实现邮箱验证(检测是否可用)
2013/05/21 Javascript
js动态修改input输入框的type属性(实现方法解析)
2013/11/13 Javascript
解决jquery中美元符号命名冲突问题
2014/01/08 Javascript
jQuery中index()的用法分析
2014/09/05 Javascript
jQuery选择器源码解读(六):Sizzle选择器匹配逻辑分析
2015/03/31 Javascript
jquery插件jquery.dragscale.js实现拖拽改变元素大小的方法(附demo源码下载)
2016/02/25 Javascript
js动态获取子复选项并设计全选及提交的实现方法
2016/06/24 Javascript
javascript解析ajax返回的xml和json格式数据实例详解
2017/01/05 Javascript
jQuery实现复选框的全选和反选
2017/02/02 Javascript
使用Require.js封装原生js轮播图的实现代码
2017/06/15 Javascript
如何为vue的项目添加单元测试
2018/12/19 Javascript
深入理解使用Vue实现Context-Menu的思考与总结
2019/03/09 Javascript
Nodejs核心模块之net和http的使用详解
2019/04/02 NodeJs
[28:42]Ti4正赛VG vs NEWBEE1
2014/07/19 DOTA
Python实现的一个简单LRU cache
2014/09/26 Python
Django数据库操作的实例(增删改查)
2017/09/04 Python
numpy.delete删除一列或多列的方法
2018/04/03 Python
Python操作rabbitMQ的示例代码
2019/03/19 Python
详解Python的循环结构知识点
2019/05/20 Python
Python tornado上传文件的功能
2020/03/26 Python
更新升级python和pip版本后不生效的问题解决
2020/04/17 Python
python操作toml文件的示例代码
2020/11/27 Python
高中生学习总结的自我评价范文
2013/10/13 职场文书
自荐信的五个重要部分
2013/10/29 职场文书
资助贫困学生倡议书
2014/05/16 职场文书
服务承诺书怎么写
2014/05/24 职场文书
厨师长岗位职责范本
2014/08/25 职场文书
2014国庆节商场促销活动策划方案
2014/09/16 职场文书
个人对照检查材料思想汇报
2014/09/26 职场文书
地方白酒代理协议书
2014/10/25 职场文书