1.項目常常須要無縫滾動效果,當時寫jq的時候用用msClass這個老插件,相對不上很好用。2.後來轉向vue在vue-awesome沒有找到好的無縫滾動插件,除了配置swiper能夠實現可是相對來講過重了,因而本身造了個輪子。javascript
3.在這分享下,當時寫這個插件的坑,本身也複習下,若是代碼上有瑕疵歡迎指出。html
html
1.solt提供默認插槽位來放置父組件傳入的htmlvue
<template> <div @mouseenter="enter" @mouseleave="leave"> <div ref="wrapper" :style="pos"> <slot></slot> </div> </div> </template>
javascript
1.animationFrame 動畫api兼容處理java
2.arrayEqual 判斷數組是否相等 來監聽data的變化來實現更新無縫滾動git
<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的更改github
commit記錄api
commit記錄數組
//本來組件調用 <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>
commit記錄app
這個問題的緣由查了比較久最後發現是當時沒有加return沒有取到定時器idless
相似上下能夠查看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吧。