1.項目常常須要無縫滾動效果,當時寫jq的時候用用msClass這個老插件,相對不上很好用。javascript
2.後來轉向vue在vue-awesome沒有找到好的無縫滾動插件,除了配置swiper能夠實現可是相對來講過重了,因而本身造了個輪子。html
3.在這分享下,當時寫這個插件的坑,本身也複習下,若是代碼上有瑕疵歡迎指出。vue
htmljava
1.solt提供默認插槽位來放置父組件傳入的htmlgit
<template>
<div @mouseenter="enter" @mouseleave="leave">
<div ref="wrapper" :style="pos">
<slot></slot>
</div>
</div>
</template>
複製代碼
javascriptgithub
2.arrayEqual 判斷數組是否相等 來監聽data的變化來實現更新無縫滾動數組
<script>
require('comutils/animationFrame') //requestAnimationFrame api
const arrayEqual = require('comutils/arrayEqual')
export default {
data () {
return {
yPos: 0,
reqFrame: null
}
},
props: {
data: { // data 數據
type: Array,
default: []
},
classOption: { //參數
type: Object,
default: {}
}
},
computed: {
pos () {
// 給父元素的style
return {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) return
cancelAnimationFrame(this.reqFrame)
},
leave () {
if (!this.options.hoverStop || this.moveSwitch) return
this._move()
},
_move () {
//滾動
this.reqFrame = requestAnimationFrame(
() => {
let h = this.$refs.wrapper.offsetHeight / 2
let direction = this.options.direction
if (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>
複製代碼
有興趣能夠看本次commit記錄 myClass.vue的更改bash
commit記錄app
//本來組件調用
<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延遲 做用保證能取到最新的html
this.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>
複製代碼
這個問題的緣由查了比較久最後發現是當時沒有加return沒有取到定時器id
相似上下能夠查看commit
import vueMyCLass from './components/myClass.vue'
let myScroll
const 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.封裝屢次調用的取消動畫方法
_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()加上開關達到效果。
若是對原生js實現相似的無縫滾動有興趣能夠留言,我抽空也能夠寫下seamless-scroll
vue-seamless-scroll發現bug或者有什麼不足望指點,感受不錯點個star吧。