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.Treeview添加右键菜单的实现代码
Oct 22 Javascript
JavaScript判断变量是否为undefined的两种写法区别
Dec 04 Javascript
javascript中的nextSibling使用陷(da)阱(keng)
May 05 Javascript
jquery ajax请求方式与提示用户正在处理请稍等
Sep 01 Javascript
IE下使用jQuery重置iframe地址时内存泄露问题解决办法
Feb 05 Javascript
jQuery中extend函数详解
Feb 13 Javascript
jquery获取css的color值返回RGB的方法
Dec 18 Javascript
AngularJS基础 ng-dblclick 指令用法
Aug 01 Javascript
JavaScript实现类似淘宝的购物车效果
Mar 16 Javascript
ES6中javascript实现函数绑定及类的事件绑定功能详解
Nov 08 Javascript
layui输入框中只允许输入整数的实现方法
Sep 18 Javascript
浅谈在vue-cli3项目中解决动态引入图片img404的问题
Aug 04 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 无限分类的树类代码
2009/12/03 PHP
PHP URL路由类实例
2013/11/12 PHP
PHP几个实用自定义函数小结
2016/01/25 PHP
IE 缓存策略的BUG的解决方法
2007/07/21 Javascript
jQuery 选择器理解
2010/03/16 Javascript
angularJS 中$attrs方法使用指南
2015/02/09 Javascript
javascript实时显示北京时间的方法
2015/03/12 Javascript
js鼠标点击按钮切换图片-图片自动切换-点击左右按钮切换特效代码
2015/09/02 Javascript
JavaScript操作URL的相关内容集锦
2015/10/29 Javascript
JavaScript事件详细讲解
2016/06/27 Javascript
原生Javascript插件开发实践
2017/01/09 Javascript
详解Vue2 无限级分类(添加,删除,修改)
2017/03/07 Javascript
原生JS+Canvas实现五子棋游戏实例
2017/06/19 Javascript
浅谈Vue.nextTick 的实现方法
2017/10/25 Javascript
npm配置国内镜像资源+淘宝镜像的方法
2018/09/07 Javascript
使用koa2创建web项目的方法步骤
2019/03/12 Javascript
Vue框架下引入ActiveX控件的问题解决
2019/03/25 Javascript
ES6模板字符串和标签模板的应用实例分析
2019/06/25 Javascript
webpack打包优化的几个方法总结
2020/02/10 Javascript
Vue 中 template 有且只能一个 root的原因解析(源码分析)
2020/04/11 Javascript
[05:45]Ti4观战指南(下)
2014/07/07 DOTA
[02:56]《DAC最前线》之国外战队抵达上海备战亚洲邀请赛
2015/01/28 DOTA
[51:36]EG vs VP 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
Django自定义插件实现网站登录验证码功能
2017/04/19 Python
Python使用遗传算法解决最大流问题
2018/01/29 Python
如何在django中实现分页功能
2020/04/22 Python
pandas数据处理之绘图的实现
2020/06/15 Python
CSS3制作hover下划线动画
2017/03/27 HTML / CSS
html svg生成环形进度条的实现方法
2019/09/23 HTML / CSS
Spanx塑身衣官网:美国知名内衣品牌
2017/01/11 全球购物
乡镇干部先进事迹材料
2014/02/03 职场文书
2015年科协工作总结
2015/05/19 职场文书
赤壁观后感(2)
2015/06/15 职场文书
田径运动会通讯稿
2015/07/18 职场文书
幼儿园2016年感恩节活动总结
2016/04/01 职场文书
SQL实战演练之网上商城数据库商品类别数据操作
2021/10/24 MySQL