vue-seamless-scroll从入坑到放弃

  • 一、vue-seamless-scroll是什么?
  • 二、使用步骤
    • 1.引入库
    • 2.具体使用
  • 三、下面就是我的踩坑之路
    • 1.vue-seamless-scroll中的事件丢失问题
    • 2. 解决事件丢失问题
    • 3.vue-seamless-scroll 中异步数据的问题
    • 4.数据时有时无解决
  • 四、 vue-seamless-scroll 源码理解
    • 1.页面结构分析
    • 2. js逻辑部分分析
      • 2.1 属性简单介绍
      • 2.2 computed属性简单介绍
      • 2.3 methods方法介绍
        • _initMove初始化方法源码分析(主要)
        • _move滚动方法源码分析(主要)
  • 五、 个人插件推荐
  • 结语
    • 参考质料

一、vue-seamless-scroll是什么?

vue-seamless-scroll是vue当前使用最多的一个列表循环滚动插件。它功能强大使用方便,这也是我为什么选择它来入坑的原因。

二、使用步骤

1.引入库

//安装vue-seamless-scroll插件
npm install vue-seamless-scroll --save
//在mian.js文件中引入
import scroll from 'vue-seamless-scroll'
Vue.use(scroll)

2.具体使用

先贴上官方示例地址

//此处为官方示例地址的demo
<template><vue-seamless-scroll :data="listData" class="seamless-warp"><ul class="item"><li v-for="item in listData"><span class="title" v-text="item.title"></span><span class="date" v-text="item.date"></span></li></ul></vue-seamless-scroll>
</template>
<style lang="scss" scoped>.seamless-warp {height: 229px;overflow: hidden;}
</style>
<script>export default {data () {return {listData: [{'title': '无缝滚动第一行无缝滚动第一行','date': '2017-12-16'}, {'title': '无缝滚动第二行无缝滚动第二行','date': '2017-12-16'}, {'title': '无缝滚动第三行无缝滚动第三行','date': '2017-12-16'}, {'title': '无缝滚动第四行无缝滚动第四行','date': '2017-12-16'}, {'title': '无缝滚动第五行无缝滚动第五行','date': '2017-12-16'}, {'title': '无缝滚动第六行无缝滚动第六行','date': '2017-12-16'}, {'title': '无缝滚动第七行无缝滚动第七行','date': '2017-12-16'}, {'title': '无缝滚动第八行无缝滚动第八行','date': '2017-12-16'}, {'title': '无缝滚动第九行无缝滚动第九行','date': '2017-12-16'}]}}}
</script>

可以看出使用的方式没什么难度,这里也贴出官网给出要注意的地方。

注意:需要给父容器一个height和:data='Array’和overfolw:hidden;左右滚动需要给ul容器一个初始化 css width。

如果以上步骤没有你都没有出错就应该可以看到效果了。

三、下面就是我的踩坑之路

1.vue-seamless-scroll中的事件丢失问题

我们的业务逻辑大概是这样的

刚开始我还以为可以开心的使用vue-seamless-scroll来做这个效果,但在实现这个滚动容器之后就发现了问题。这里面的点击事件时有时无。

只能说我心态崩了,这是什么情况?

打开F12参看他的滚动容器,发现了他的一个惊天的秘密。

  原来他的实现方式是自己复制了一份相同的dom,贴在了原来dom的后面来实现循环滚动的。看到这里我就猜到问题出现在他复制出来的dom中。又重新试了一下所有的点击事件,果然有一个dom容器中的点击事件完全没有问题,而另一个中的点击事件没有任何相应。

碰到这个问题的同学我想肯定不会很少吧。估计都会自己去重新实现一个组件了吧。别急往下看。

2. 解决事件丢失问题

使用 @click="scrollClick($event)"的方式在外层父元素上添加点击事件来获取点击的子dom的方式,再获取其中的数据来进行下一步的事件处理。

<divclass="v-scroll"v-if="isScroll(item.title)"@click="scrollClick($event)"><vue-seamless-scroll :data="newData" class="list-style ">...</vue-seamless-scroll></div>

父容器的事件处理

   scrollClick(e) {//通过 e.target获取点击的dom//通过e.target.dataset获取vuedom中的自定义属性let data = e.target.dataset;this.$emit("supervise-detail", data.name, data.type);}

在要监听点击事件的子dom中的处理

 <span:title="itemObj.number2":data-name="itemObj.name":data-type="'all'">/{{ itemObj.number2 }}</span>
</span>

这里使用data-属性的方式来进行属性绑定,以方便父容器的点击事件中获取该属性。
这样就可以通过其中的属性来处理相应的事件内容了。

3.vue-seamless-scroll 中异步数据的问题

这个项目的业务逻辑是这样的,

这个问题也是比较头痛的,当时看到的效果是滚动容器中显示的数据时有时无。

  原因是其中的数据可能是多个接口返回回来的。所以vue-seamless-scroll的data更新达不到统一。所以在有些数据还没有返回时vue-seamless-scroll就将dom复制出来,而复制出来的dom中的数据不是响应式的,所有就会出现数据时有时无的情况。

这也使我想到了其中的解决办法。接着往下看。

4.数据时有时无解决

1、使用promise、generator或async的方式来解决异步数据的问题
这里可以使用promise、generator或async的方式来解决异步的问题,使接口顺序调用。 具体实现就不贴出来了。网上有很多。然后在最后一个接口数据成功返回时,添加一个是否已全部加载的字段。并为其赋值为true。以用来在vue-seamless-scroll上使用v-if来控制vdom的渲染。
2、使用key来使vue-seamless-scroll重新渲染
原理都是一样的,就是通过vue-seamless-scroll的渲染来使其重新复制已经赋好值的dom。具体使用如下:
    <vue-seamless-scroll ref="scrollModule" :data="newData" class="list-style " :key="scrollKey">......</vue-seamless-scroll>

在vue的data中添加一个scrollKey,然后绑定在vue-seamless-scroll 的组件上。

  methods: {......upDateKey() {this.scrollKey++;},},

  然后在methods中添加一个upDateKey方法,使key值更新。 这时就可以在该组件中或在其父主键中使用$refs.设置的名称.upDateKey()来修改key值,使其重新渲染。

关于key的使用不清楚的同学可以看一下下面的文章。

Vue 中 Key的详细解释.

这时只需这接口数据成功返回时调用其方法就可以了。
你以为这就完了?再来扒一扒它的源码吧

四、 vue-seamless-scroll 源码理解


  在已经引入的项目中,打开node_module库就可以看到其源码了。结构也很简单,主要的就index.js与myClass.vue文件。 index.js文件是写vue插件时候的一些配置。感兴趣的可以搜一搜vue插件开发。主要的就是myClass.vue文件。

1.页面结构分析

查看如下:


<template><div ref="wrap"><div :style="leftSwitch" v-if="navigation" :class="leftSwitchClass" @click="leftSwitchClick"><slot name="left-switch"></slot></div><div :style="rightSwitch" v-if="navigation" :class="rightSwitchClass" @click="rightSwitchClick"><slot name="right-switch"></slot></div><divref="realBox":style="pos"@mouseenter="enter"@mouseleave="leave"@touchstart="touchStart"@touchmove="touchMove"@touchend="touchEnd"><div ref="slotList" :style="float"><slot></slot></div><div v-html="copyHtml" :style="float"></div></div></div>
</template>

  这是它全部的页面结构代码,是不是很简洁。在这部分我们主要关注的就是ref="realBox"的div标签。这个就是它实现循环滚动的主要dom容器。可以看到这个dom容器上绑定了很多事件,这个就是用来监听pc端的鼠标移入移出与移动端的手指触摸事件。在继续看他里面的结构。很明显可以看到有一个包含slot标签的dom容器,和一个带有v-html="copyHtml"的dom容器。

  看到这里也初步的证明了我们前面的猜想。使用slot插入进来的dom就是数据拥有响应式监听,事件保存完好的源dom(我们自己插入的dom)。而使用v-html 复制出来的dom,就是数据没有响应式,事件丢失的dom。OK继续往下看。

2. js逻辑部分分析

2.1 属性简单介绍


上面这一部分都是一些基础参数,根据图上注释理解即可。继续走着。

2.2 computed属性简单介绍

接下来就是它的computed属性。

  看第一个注释从leftSwitchState到rightswitch都是在使用它的switch功能时用到的,这偏文章就不做解释了。而且基本上都是一两行代码,注释也写的很清楚。所以感兴趣的同学可以自行去研究。

接下来两个就是比较重要的属性:

  float属性就是控制两个滚动容器是水平排列还是垂直排列的动态样式。
pos属性,他就是控制滚动容器的滚动方向与速度的动态样式。

ok继续往下看


这些属性就比较重要了下面来一个一个讲

  defaultOption属性就是滚动容器的默认配置,如果一些属性没用自行配置就会取其中默认的配置。

  options属性就是将传入的配置属性与默认属性进行合并,得到的最终配置参数的。

  navigation属性是控制switch模式下的切换按钮是否显示的。
  autoPlay属性是控制容器是否自动滚动的,这里代码也很简单。如果为switch模式(是否显示switch的切换按钮)默认不自动滚动,而不是swich模式就取传入进来的值来判断是否自动滚动。

继续走着

这个就是剩下的computed属性,上面的截图中也做了一些简单的解释。取几个主要的我在说一下。

  baseFontSize属性是计算字体的基础大小的,主要是用来为realSingleStopWidth与realSingleStopHeight属性方便计算对应的宽度与高度。

  step属性是计算滚动的步长(距离)的。在获取自身的步长时,也将单步滚动的步长计算了看出来。这里作者也对应做出了相对应的警告,警告的内容就是上面的console.error中的内容。

  看到这里一些基本的属性,就全部看完了。是不是没什么太复杂的地方。好我们继续往下看。我们下一步该考虑使用这个组件时他是从哪一步开始执行的,我想聪明的你很快就已经想到了。没错就是mounted钩子函数。

2.3 methods方法介绍

那我们继续走着


  这不要太简单,我本来想只截下mounted中的代码,可是只有一行。所以把methods中的方法也贴出来了。那我们就先大致了解一下(我们先猜测一下)。

  leftSwitchClick 与 rightSwitchClick 方法看名字就能知道这是switch模式下,左边按钮与右边按钮的点击事件。

  _cancle 方法的英文意思时缓存,那我们就将它先理解为一些缓存操作相关的方法。

  touchStart、touchMove、touchEnd 方法,我想看名字你们也应该猜到了这是移动端相关的手指触摸事件。

  enter、leave方法,这两个我们之前是不是在哪见过。是的它们就是滚动主容器上绑定的鼠标事件。忘记的可以回过头去,在讲解template结构上看一下。

  _move方法,名字是移动的意思。那我们就先将它理解为移动相关的函数。

  _initMove方法,这个就是我们刚才mounted中的初始化函数。我们等下就会详细讲解。

  _dataWarm方法,这个看意思是数据警告,如果展示还不理解先放一下。等下我们回头再看。

  _startMove与_stopMove方法,这个也根据名字见他们风别理解为开始移动与停止移动的方法。

看到这里你可能会有这样的感触,原来命名方法或属性的时候。名字取得与功能相对贴进一点是多么的友好!闲话少说,我们进入主要的部分。

_initMove初始化方法源码分析(主要)


  从全局来看,_initMove方法使用vue中的nextTick方法进行异步回调处理。然后在器方法中获取一些基础的属性,根据这些属性进行一些滚动之前的初始化。

  先从第三四行代码开始看,它调用了一下_dataWarm方法与清空copyHtml的操作。这里有遇到了这个_dataWarm方法,心急的同学可能会马上去看一下里面的内容。不过我喜欢先读完一整个方法在去看一些它使用过而我没有了解的方法。那我们就继续往下走。

第一个方框部分:
  这段代码的意思是,如果为水平滚动这通过ref属性来获取整个容器的宽高与插入进来dom容器的宽度。代码如下

this.height = this.$refs.wrap.offsetHeight
this.width = this.$refs.wrap.offsetWidth
let slotListWidth = this.$refs.slotList.offsetWidth

再根据autoPlay属性来修正滚动容器的宽度与滚动父容器的宽度,最后再根据slotListWidth 设置内容的实际宽度realBoxWidth。

  第二个方框部分。是根据autoplay属性来动态切换动画的样式。

  第三个方框部分。是比较主要的,他通过scrollSwitch来判断是否可以滚动,如果可以就重新开启一个计时器。并将插入进来的dom使用$ref的方式获取其中的dom保存下来。然后通过一个settimeout来实现异步获取父滚动容器的高度。并调用滚动函数开始滚动。否则就会调用_cancle方法做一些缓存处理。并初始化yPos与xPos属性。

看到这里肯定有一些心急的同学把这个_cancle方法看完了,那我这里也回头看下这个处理缓存的函数吧。

这里面只有一行代码,就是清除使用requestAnimationFrame方法所造成的缓存。

那我们在回头看一下_dataWarm方法。

看的出来作者还是很贴心的。接下来就是核心的_move方法了。

_move滚动方法源码分析(主要)

由于_move方法的代码比较多,所以这里将逐层讲解。这样也可以更清晰的看出该部分的结构。便于我们更好的阅读源码。

  这里先看最外层,作者标出的注释也比较贴心。所以这里我们理解起来也很容易。他先使用了一个开关变量来模拟鼠标的hover事件,实现停止与滚动的切换。接着就是滚动之前的缓存清理操作。

  最后就是这个滚动函了数实现滚动的主逻辑。这里他使用了requestAnimationFrame方法来实现滚动的效果,在requestAnimationFrame中的这个回调方法就是滚动的主函数。在这个函数后还使用bind方法将this保存了下来,以防回调函数中的this指向问题。

  不得不提一下要注意使用requestAnimationFrame与settimeout来实现动画的区别了。具体的区别也是比较容易搜到的。我也提供了一下相关的质料。

setTimeout, setInterval 与 requestAnimationFrame 隐藏的各种坑


  这部分就是我们重点关注的对象,来看看这里可是怎么实现的。

  虽然其中的部分代码没有展开,但我们还是可以从注释与定义的属性名中猜到他们是做什么的。截图中我也做了对应的注释。函数的开始通过绑定this来获取滚动容器的真实宽度与高度,滚动的方向,单步停止等待时间、步长。在跟据这些属性做出下面对应的一些逻辑操作(根据滚动方向与单步滚动模式下做出的一些对应的逻辑操作)。


  先展开框出来的第一部分。截图中我没有添加注释,因为这里也是比较简单。我们先把带有注释的上与左一同理解,下与右一同理解。他们只有滚动变化的属性(xPos与yPos)不同其他逻辑都是一模一样的。那这里我就把带有注释上与下拿出来详细解释一下。

上:如果y轴方向滚动距离的绝对值(yPos的滚动距离)大于等于父容器高度的一半,就将y轴滚动的位置赋值为起始位置。再触发外部的ScrollEnd方法。然后就是根据滚动步长来计算y轴滚动的位置。

下:如果y轴方向滚动距离的绝对值(yPos的滚动距离)小于等于滚动的起始位置时,就将y轴滚动的位置赋值为父容器高度的一半的负值。再触发外部的ScrollEnd方法。然后就是根据滚动步长来计算y轴滚动的位置。


我们来根据这张图理解一下。假设页面刚好滚动到如图这种情况,然后继续向上滚动,刚好滚动到我们插入进来的dom完全隐藏时(刚好滚动了一个dom的高度)。他就会将滚动的位置初始为起始的位置。如此往复,就达到了循环滚动的效果。

向下滚动也可以想象一下滚动到当前位置时,我们插入进来的dom上面没有dom了,所以就将位置向前了一个dom容器的高度。在如此往复。

接下来就是单步滚动模式下的逻辑了。

  这里也是非常简单的。先将单步滚动的定时器清除掉。在进入判断时宽度的单步滚动模式还是高度单步滚动模式。看判断是宽度还是高度单步滚动的两个if判断中的内容,是不是一模一样。所以不要怕干就完了。

  同样的先判断realSingleStopHeight或realSingleStopWidth是否存在,再通过滚动的距离对realSingleStopHeight或realSingleStopWidth取余,看是否小于步长。如果小于就进入单步定时器进行等待滚动。否则直接调用move方法进行滚动。是不是很简单。

  到这里我们对vue-seamless-scroll中的核心源码就全部讲解完毕了。如果有同学还对它其他部分的源码感兴趣,可以根据这个分析思路继续去了解一下。相信你们的能力一定可以的。

五、 个人插件推荐

  在这篇文章的最后我也带上一点私货。那就是我自己实现的一个循环滚动插件。这个插件虽然没有vue-seamless-scroll这个功能丰富,但他将vue-seamless-scroll不足的地方都弥补了一下。包括复制出来dom的点击事件,及数据的响应依赖都保存了下来。还增加了鼠标的滚轮模式。避免与滚动中的数据交互时的尴尬问题(例:滚动过去的数据要在等下一轮滚动才能交换)。

插件地址:
https://www.npmjs.com/package/@david-j/vue-j-scroll
效果图:


图中鼠标悬浮再滚动容器上仍何以使用滚轮控制滚动。如果想与滚动过去的某一条数据交互就不需要再等一个轮回了。是不是解决了某些同学的燃眉之急。如果感兴趣并且使用了该插件,还请各位同学多多反馈。我会积极的对插件中的问题及时修复。功能上也会相对做出扩展的。

结语

好了这篇文章到这里就完全结束了,文章中我的一些个人看法较多。如果各位大佬们感觉有什么不足之处可尽情提出。我将及时的做出修改与答复。希望我们同学习,同进步。以后有时间我还会在写一些其他方面的文章的。希望大家多多支持。

参考质料

vue-seamless-scroll使用中遇到关于click的问题.

Vue 中 Key的详细解释.

setTimeout, setInterval 与 requestAnimationFrame 隐藏的各种坑

vue-seamless-scroll 从入坑到放弃相关推荐

  1. 微信小程序从入坑到放弃二十二:完美兼容安卓和ios手机的底部评论框

    摘要: 不管是在普通的H5页面还是在微信小程序中,底部输入框一直是一大难题,因为键盘会在某些情况下遮挡信输入框一部分!值得庆幸的是:在微信小程序中,我们可以通过监听bindfocus来获取键盘的高度, ...

  2. 微信小程序从入坑到放弃二十九:一个小场景搞懂冒泡事件bindtap和catchtap的区别

    摘要: 在微信小程序中,bindtap事件会产生冒泡,若不加以拦截,会一直冒泡到顶端.在某些情况下,一次点击会触发若干点击事件.为了防止冒泡,使用catchtap即可解决问题.在有全屏半透明背景的弹出 ...

  3. PowerBuilder从入坑到放弃(一)第一个HelloWorld程序

    前言 不少小伙伴可能都是由于PowerBuilder 来关注公众号的,网上关于PowerBuilder的资料确实是少之又少. – – – 来了的小伙伴可能发现了,博主怎么一篇PowerBuilder ...

  4. 通过CVE-2017-17215学习路由器漏洞分析,从入坑到放弃

    1.基本信息: 2017/11/27,Check Point 软件技术部门报告了一个华为 HG532 产品的远程命令执行漏洞(CVE-2017-17215),Mirai的升级版变种中已经使用该漏洞.看 ...

  5. 大数据Hive其实一点都不难,从入坑到放弃?不存在的

    Hive 首先,我们来介绍一下什么是Hive.有些人不仅会想,Hive不就是写SQL的吗.没错,Hive和SQL的语法结构很像,其实,二者没有多大区别,甚至可以这样讲,Hive就是写SQL.但是,问题 ...

  6. 最好的Vue组件库之Vuetify的入坑指南(持续更新中)

    目录 安装Vuetify 文档结构 快速入门 特性 样式和动画 首先先声明,个人不是什么很牛逼的大佬,只是想向那些想入坑Vuetify的前端新手或者嫌文档太长不知如何入手的人提供一些浅显的建议而已,能 ...

  7. Vue React Angular之三国杀,web前端入坑第六篇 上

    「 懒癌引发血案 」 目前前端技术栈发生了翻天覆地的变化,上篇刚写了只会jquery 要失业,再不学新的你就要被淘汰,虽然有点危言耸听,不过现实情况确实是这样. vue.react.angular对比 ...

  8. Vue、React、Angular之三国杀,web前端入坑第六篇(上)

    「 懒癌引发血案 」 目前前端技术栈发生了翻天覆地的变化,上篇刚写了只会jquery 要失业,再不学新的你就要被淘汰,虽然有点危言耸听,不过现实情况确实是这样. vue.react.angular对比 ...

  9. 入坑VUE的一点心得

    前言 本人专业做后台,有一定前端基础,最近想不通学了几天VUE,由于入坑过程有点小心酸, 想想还是记下来吧,一则是总结,说的不一定对,再则看看能不能给后入坑的人一点帮助吧 1.关键地址,具体作用后面会 ...

最新文章

  1. Jackson学习笔记(三)转
  2. eeglab教程系列(13)-学习和删除ICA组件
  3. 关于批量插入数据之我见(100万级别的数据,mysql) (转)
  4. git clone 出错SSL certificate problem, verify that the CA cert is OK.
  5. what you want from a relationship?
  6. 查看selenium python的api小记录
  7. 链接(跳转)router-link 和 路由实例Router
  8. 使用Google App Engine开始新的网站开发学习
  9. toString 方法
  10. 字节跳动经营范围新增销售电子产品家用电器等
  11. thinkphp中I(parm)用法的注意事项
  12. 复制oracle9i数据库,Oracle 10g 中Duplicate 复制数据库
  13. winform datagridview 打印预览
  14. windows计算器_计算Windows计算器中的错误
  15. Error:(292, 40) java: -source 1.5 中不支持 diamond 运算符 (请使用 -source 7 或更高版本以启用 diamond 运算符) ........
  16. php怎么把字符转成大写,php将字符串全部转换成大写或者小写的方法
  17. 菜鸟的一些常用快捷键的使用总结
  18. mysql基本用法笔记
  19. 2019年—BAT大型互联网企业刚出炉的一套面试题(Java岗)
  20. 最新WIN10系统封装教程2019系列(五)——使用工具优化与清理

热门文章

  1. C++:CompareNoCase函数
  2. 华为发布首款鸿蒙系统终端,首款鸿蒙系统终端新闻 华为正式发布鸿蒙系统
  3. 万达商管再闯IPO大门
  4. JVM 内存模型面试题目
  5. 用上office365后配合RaiDrive效率如何提高
  6. 途牛android源码,途牛,Android 开发工程师,一面,攒人品
  7. NumPy 快速入门系列:应用统计学基础概念、相关统计指标与NumPy的实现
  8. JSP-学生管理系统
  9. 在 VMware Workstation 16 Pro 中安装 Ubuntu Server 22.04.1 并配置静态 IP 地址
  10. Nagios-config