寫插件的初衷
1.項目經常需要無縫滾動效果,當時寫jq的時候用用msClass這個老插件,相對不上很好用。2.后來轉向vue在vue-awesome沒有找到好的無縫滾動插件,除了配置swiper可以實現但是相對來說太重了,于是自己造了個輪子。
3.在這分享下,當時寫這個插件的坑,自己也復習下,如果代碼上有瑕疵歡迎指出。
源碼參考 vue-seamless-scroll
1.簡單的實現上下滾動基本版(最初版)
html
1.solt提供默認插槽位來放置父組件傳入的html
<div @mouseenter="enter" @mouseleave="leave"><div ref="wrapper" :style="pos"><slot></slot></div></div>
</template>
javascript
1.animationFrame 動畫api兼容處理
2.arrayEqual 判斷數組是否相等 來監聽data的變化來實現更新無縫滾動
<script>require('comutils/animationFrame') //requestAnimationFrame apiconst arrayEqual = require('comutils/arrayEqual')export default {data () {return {yPos: 0,reqFrame: null}},props: {data: { // data 數據type: Array,default: []},classOption: { //參數type: Object,default: {}}},computed: {pos () {// 給父元素的stylereturn {transform: `translate(0,${this.yPos}px)`}},defaultOption () {return {step: 1, //步長limitMoveNum: 5, //啟動無縫滾動最小數據數hoverStop: true, //是否啟用鼠標hover控制direction: 1 //1 往上 0 往下}},options () {// 合并參數return Object.assign({}, this.defaultOption, this.classOption)},moveSwitch () {//判斷傳入的初始滾動值和data的length來控制是否滾動return this.data.length < this.options.limitMoveNum}},methods: {enter () {if (!this.options.hoverStop || this.moveSwitch) returncancelAnimationFrame(this.reqFrame)},leave () {if (!this.options.hoverStop || this.moveSwitch) returnthis._move()},_move () {//滾動this.reqFrame = requestAnimationFrame(() => {let h = this.$refs.wrapper.offsetHeight / 2let direction = this.options.directionif (direction === 1) {if (Math.abs(this.yPos) >= h) this.yPos = 0} else {if (this.yPos >= 0) this.yPos = h * -1}if (direction === 1) {this.yPos -= this.options.step} else {this.yPos += this.options.step}this._move()})},_initMove () {if (this.moveSwitch) {cancelAnimationFrame(this.reqFrame)this.yPos = 0} else {this.$emit('copyData') //需要copy復制一份 emit到父元素 后期版本這里已經優化if (this.options.direction !== 1) {setTimeout(() => {this.yPos = this.$refs.wrapper.offsetHeight / 2 * -1}, 20)}this._move()}}},mounted () {this._initMove()},watch: {//監聽data的變化data (newData, oldData) {if (!arrayEqual(newData, oldData.concat(oldData))) {cancelAnimationFrame(this.reqFrame)this._initMove()}}}}
</script>
1.1 優化1: 新增配置openWatch 是否開啟data監控實時刷新
有興趣可以看本次commit記錄 myClass.vue的更改
1.2 優化2: 新增配置singleHeight waitTime參數 控制是否單步滾動
commit記錄
1.3 優化3:添加對移動端touch事件滾動列表支持
commit記錄
1.4 優化4: 去掉了emit回調(簡化初始化)
//原本組件調用
<my-class :data="listData" :class-option="classOption" @copy-data="listData = listData.concat(listData)">
//簡化后組件調用
<my-class :data="listData" :class-option="classOption" class="warp">
用js的來復制一份innerHtml來代替之前的做法簡化使用
//this.$emit('copyData')timer = setTimeout(() => { //20ms延遲 作用保證能取到最新的htmlthis.copyHtml = this.$refs.slotList.innerHTML}, 20)// template<template><div @mouseenter="enter" @mouseleave="leave" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd"><div ref="wrap" :style="pos"><div ref="slotList" :style="float"><slot></slot></div><div v-html="copyHtml" :style="float"></div></div></div>
</template>
commit記錄
1.5 bug1: 解決ie9下animationFrame報錯的bug
這個問題的原因查了比較久最后發現是當時沒有加return沒有取到定時器id
1.6 優化5:添加左右無縫滾動
類似上下可以查看commit
1.7 Vue.use() 提供install全局注冊
import vueMyCLass from './components/myClass.vue'let myScrollconst defaultComponentName = 'vue-seamless-scroll'// expose component to global scope
if (typeof window !== 'undefined' && window.Vue) {Vue.component('vue-seamless-scroll', vueMyCLass)
} else {myScroll = {install: function (Vue, options = {}) {Vue.component(options.componentName || defaultComponentName, vueMyCLass)}}}export default myScroll
1.8 bug 解決了touchMove頻繁快速操作導致單步滾動失效bug 和部分代碼優化
//1.封裝多次調用的取消動畫方法
_cancle: function _cancle() {cancelAnimationFrame(this.reqFrame || '');},
//2.touchMove頻繁快速操作導致滾動錯亂bug
_move () {this._cancle() //進入move立即先清除動畫 防止頻繁touchMove導致多動畫同時進行}
//3.生命周期結束前取消動畫
beforeDestroy () {this._cancle()
}
//4.修復不傳參數報警告的bug
props: {data: {type: Array,default: () => {return []}},classOption: {type: Object,default: () => {return {}}}}
//5.Fixing a bug. add a overflow:hidden on the child element
部分人喜歡用margin-top如果沒有overflow等限制會導致我里面計算高度和實際有些許差距導致最后效果到臨界位置有輕微抖動
//默認加上了overflow: 'hidden'
computed: {float () {return this.options.direction > 1 ? {float: 'left', overflow: 'hidden'} : {overflow: 'hidden'}},pos () {return {transform: `translate(${this.xPos}px,${this.yPos}px)`,transition: `all ease-in ${this.delay}ms`,overflow: 'hidden'}}
}
//6.新增單步滾動也能hover停止的功能
之前因為單步滾動內置了延遲執行this._move()默認單步限制了鼠標懸停停止無縫滾動,后來通過給this._move()加上開關達到效果。
commit
TKS
如果對原生js實現類似的無縫滾動有興趣可以留言,我抽空也可以寫下seamless-scroll
vue-seamless-scroll發現bug或者有什么不足望指點,感覺不錯點個star吧。
原文地址:https://segmentfault.com/a/1190000013010808