w-ui
Version:
A Vue.js 2.0 UI Toolkit for mobile
394 lines (377 loc) • 11.4 kB
JavaScript
require('./infinitescroll.less')
export default {
name: 'w-infinite-scroll',
data () {
return {
drag: false,
startX: 0,
startY: 0,
startTime: 0,
currentX: 0,
currentY: 0,
offsetW: 0,
offsetH: 0,
currentPage: 0,
pageInfo: [],
lastCount: 0,
toBottom: false,
ww: 0,
hh: 0
}
},
props: {
direction: {
type: String,
default: 'h'
},
containsIframe: {
type: Boolean,
default: false
},
pageCount: {
type: Number,
default: Infinity,
required: true
},
canScroll: {
type: Function
}
},
methods: {
touchstart (e) {
this.drag = true
let tar = e.touches || [e]
tar = tar[0]
this.startX = tar.pageX
this.startY = tar.pageY
this.startTime = Date.now()
this.offsetW = 0
this.offsetH = 0
this.currentX = this.getCurrentX()
this.currentY = this.getCurrentY()
},
touchmove (e) {
if (this.drag) {
let tar = e.changedTouches || [e]
tar = tar[0]
if (this.direction === 'h') {
let offsetX = tar.pageX - this.startX
let offsetY = tar.pageY - this.startY
if (Math.abs(offsetX) > Math.abs(offsetY)) {
this.offsetW = offsetX
let lastx = this.currentX + offsetX
if (lastx > 0 || lastx < this.minsw) {
} else {
this.translateTo(lastx, 0, 0, true)
e.preventDefault()
e.stopPropagation()
}
}
} else {
if (this.canScroll && !this.canScroll()) {
return;
}
let offsetX = tar.pageX - this.startX
let offsetY = tar.pageY - this.startY
if (Math.abs(offsetY) > Math.abs(offsetX)) {
this.offsetH = offsetY
let lasty = this.currentY + offsetY
if (lasty > 0 || lasty < this.minsh ) {
} else {
this.translateTo(0, lasty, 0, true)
e.preventDefault()
e.stopPropagation()
}
}
}
}
},
touchend (e) {
if (this.drag) {
this.drag = false
if (this.direction === 'h') {
let curx = this.getCurrentX()
if (curx > this.maxsw) {
this.bounceBack(this.maxsw, 0, 600)
} else if (curx < this.minsw) {
this.bounceBack(this.minsw, 0, 600)
} else {
this.momentumMove(e)
}
} else {
let cury = this.getCurrentY()
if (cury > this.maxsh) {
this.bounceBack(0, this.maxsh)
} else if (cury < this.minsh) {
this.bounceBack(0, this.minsh)
} else {
this.momentumMove(e)
}
}
}
},
bounceBack (x, y, t) {
let ti = t || 300
this.translateTo(x, y, ti)
},
getDamping (s) {
let max = 0
let min = this.minsw
if (this.direction === 'v') {
min = this.minsh
}
if (s > max) {
return Math.round(s/4)
} else if (s < min) {
return Math.round(min - (min - s ) / 4)
} else {
return s
}
},
getCurrentX () {
let cx = '0'
let trans = this.$refs.box.style.transform
if (trans) {
let mats = trans.replace(/\s/g, '').match(/translate3d\(([^,]+),(?:.*?)\)/)
if (mats && mats.length > 1) {
cx = mats[1].replace('px', '')
}
}
return Number.isNaN(cx) ? 0 : parseInt(cx)
},
getCurrentY () {
let cy = '0'
let trans = this.$refs.box.style.transform
if (trans) {
let mats = trans.replace(/\s/g, '').match(/translate3d\((?:.*?),(.*?),(?:.*?)\)/)
if (mats && mats.length > 1) {
cy = mats[1].replace('px', '')
}
}
return Number.isNaN(cy) ? 0 : parseInt(cy)
},
momentumMove (e) {
let difft = Date.now() - this.startTime
if (this.direction === 'h') {
let curx = this.getCurrentX()
this.stepOneItem(curx, 0, 300)
} else {
let cury = this.getCurrentY()
this.stepOneItem(0, cury, 300)
}
},
stepOneItem (curx, cury, t) {
let lastP = this.currentPage
if (this.direction === 'h') {
if (Math.abs(this.offsetW) >= this.ww / 6) {
let last
if (this.offsetW < 0) {
if (this.updatePageIndex('forward')) {
last = -this.currentPage * this.ww
} else {
let back = -lastP * this.ww
this.translateTo(back, 0, t)
}
} else {
if (this.updatePageIndex('backward')) {
last = -this.currentPage * this.ww
} else {
let back = -lastP * this.ww
this.translateTo(back, 0, t)
}
}
this.translateTo(last, 0, t)
this.$emit('change', this.currentPage, lastP)
} else {
let back = -lastP * this.ww
this.translateTo(back, 0, t)
}
} else {
if (Math.abs(this.offsetH) >= this.hh / 6) {
let last
if (this.offsetH < 0) {
if (this.updatePageIndex('forward')) {
last = -this.currentPage * this.hh
} else {
let back = -lastP * this.hh
this.translateTo(0, back, t)
}
} else {
if (this.updatePageIndex('backward')) {
last = -this.currentPage * this.hh
this.scrollToBottom()
} else {
let back = -lastP * this.hh
this.translateTo(0, back, t)
}
}
this.translateTo(0, last, t)
this.$emit('change', this.currentPage, lastP)
} else {
let back = -lastP * this.hh
this.translateTo(0, back, t)
}
}
},
updatePageIndex (dir) {
if (dir === 'forward') {
let next = this.currentPage + 1
if (next >= this.pageCount) {
return false
} else {
this.currentPage = next
}
} else if (dir === 'backward') {
let prev = this.currentPage - 1
if (prev < 0) {
return false
} else {
this.currentPage = prev
}
}
return true
},
setCurrent (index) {
this.$nextTick(() => {
if (index >= 0 && index < this.pageCount) {
this.currentPage = index
if (this.direction === 'h') {
let last = -index * this.ww
this.translateTo(last, 0, 300)
} else {
let last = -index * this.hh
this.translateTo(0, last, 300)
}
}
})
},
translateTo (x, y, t, immediately) {
let time = t || 300
time < 300 && (time = 300);
time > 600 && (time = 600);
if (immediately) {
this.$refs.box.style.webkitTransition = 'none'
this.$refs.box.style.transition = 'none'
} else {
// this.$refs.box.style.webkitTransition = `${time}ms all cubic-bezier(0.1, 0.57, 0.1, 1)`
// this.$refs.box.style.transition = `${time}ms all cubic-bezier(0.1, 0.57, 0.1, 1)`
this.$refs.box.style.webkitTransition = `${time}ms all linear`
this.$refs.box.style.transition = `${time}ms all linear`
// this.$refs.box.style.webkitTransition = `${time}ms all cubic-bezier(0.7, 0, 1, 1)`
// this.$refs.box.style.transition = `${time}ms all cubic-bezier(0.7, 0, 1, 1)`
// this.$refs.box.style.webkitTransition = `${time}ms all cubic-bezier(0, 0, 0, 0.7)`
// this.$refs.box.style.transition = `${time}ms all cubic-bezier(0, 0, 0, 0.7)`
}
this.$refs.box.style.webkitTransform = `translate(${x}px, ${y}px)`
this.$refs.box.style.transform = `translate3d(${x}px, ${y}px, 0)`
},
scrollToBottom () {
this.toBottom = true
},
checkScroll () {
if (this.offsetH > 0) {
// down
if (this.currentPage === 0) {
return false
}
if (this.currentPage < this.pageCount) {
return true
}
} else {
// up
if (this.currentPage >= this.pageCount) {
return false
}
if (this.currentPage > 0) {
return true
}
}
return false
}
},
render (h) {
if (!this.pageInfo[this.currentPage]) {
this.pageInfo[this.currentPage] = ({
startIndex: this.currentPage - 1 < 0 ? 0 : this.currentPage - 1,
endIndex: this.currentPage + 1 >= this.pageCount ? this.pageCount - 1 : this.currentPage + 1
})
}
let size, cls, sly
if (this.direction === 'h') {
size = this.ww
cls = 'wui-infinite-scroll-h'
sly = {width: this.ww + 'px'}
} else if (this.direction === 'v') {
size = this.hh
cls = 'wui-infinite-scroll-v'
sly = {height: this.hh + 'px'}
}
if (size) {
return (
<div class={cls}>
<div class='wui-infinite-list' ref='list'>
<div class='wui-infinite-container' ref='box'>
{
this.$slots.default.map((vnode, i) => {
let pi = this.pageInfo[this.currentPage]
if (pi && vnode.tag) {
if (i >= pi.startIndex && i <= pi.endIndex) {
return (
<div class='wui-infinite-item' style={sly}>
{ vnode }
{this.containsIframe ? <div class='wui-infinite-item-mask' /> : ''}
</div>
)
}
}
return <div class='wui-infinite-item' style={sly}></div>
})
}
</div>
</div>
</div>
)
} else {
return (
<div class={cls}>
<div class='wui-infinite-list' ref='list'>
<div class='wui-infinite-container' ref='box'></div>
</div>
</div>
)
}
},
updated () {
if (this.toBottom) {
let child = this.$slots.default[this.currentPage]
child.children.forEach(item => {
let ins = item.componentInstance
if (ins && ins.$options.name === 'w-scroll-tab') {
ins.scrollToBottom()
return false
}
})
this.toBottom = false
}
},
mounted () {
this.$nextTick(() => {
this.ww = this.$el.offsetWidth
this.hh = this.$el.offsetHeight
this.maxsw = 0
this.maxsh = 0
this.minsw = (1 - this.pageCount) * this.ww
this.minsh = (1 - this.pageCount) * this.hh
this.$el.addEventListener('touchstart', this.touchstart, {passive: true})
this.$el.addEventListener('mousedown', this.touchstart, {passive: true})
this.$el.addEventListener('touchmove', this.touchmove, false)
this.$el.addEventListener('mousemove', this.touchmove, false)
this.$el.addEventListener('touchend', this.touchend, {passive: true})
this.$el.addEventListener('mouseup', this.touchend, {passive: true})
})
},
destroyed () {
// window.removeEventListener('touchend', this.touchmove, false)
// window.removeEventListener('mouseup', this.touchend, false)
}
}