tqpulldownrefresh
Version:
180 lines (139 loc) • 4.47 kB
JavaScript
import { nextFrame, cancelFrame, slideTo } from './animate.js'
const masDragDistance = 300 // 最大拖动距离
let timer = null
export default class TQPullDownRefresh {
constructor(onPullStart, onReleaseToRefresh, onPulling) {
this.state = {
x: 0,
y: 0,
endY: 0,
ladderDistance: 0, // 阶梯下降时阶梯的数值
lastY: 0,
rate: 1,
isRunning: false
}
this.containerRef = null
this.areaRect = null
this.refresherRef = null
this.refresherRect = null
this.onPullStart = onPullStart
this.onReleaseToRefresh = onReleaseToRefresh
this.onPulling = onPulling
// 手动创建 dom
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = './refresh.css'
document.head.appendChild(link)
this.containerRef = document.createElement('div')
this.containerRef.className = 'container'
document.body.appendChild(this.containerRef)
this.refresherRef = document.createElement('div')
this.refresherRef.className = 'refresher'
this.containerRef.appendChild(this.refresherRef)
const img = document.createElement('img')
img.src = './refresh.png'
img.style.width = '100%'
img.style.height = '100%'
this.refresherRef.appendChild(img)
this.containerRef.addEventListener('touchstart', this.touchStart.bind(this))
this.containerRef.addEventListener('touchmove', this.touchMove.bind(this))
this.containerRef.addEventListener('touchend', this.touchEnd.bind(this))
}
prepare(touch) {
this.refresherRect = this.refresherRef.getBoundingClientRect()
this.state.x = this.refresherRect.left
this.state.y = this.refresherRect.top
this.state.lastY = touch.clientY
this.state.isRunning = true
}
destroy() {
this.containerRef.removeEventListener('touchstart', this.touchStart.bind(this))
this.containerRef.removeEventListener('touchmove', this.touchMove.bind(this))
this.containerRef.removeEventListener('touchend', this.touchEnd.bind(this))
}
// 记录日志:动画结束的时机
rotate(angle) {
console.log('旋转动画开始')
cancelFrame(timer)
this.animate(angle)
}
animate(angle) {
this.refresherRef.style.transform = `translate(0px, ${this.state.y}px) rotate(${angle}deg`
angle = angle === -360 ? 0 : angle - 5
timer = nextFrame(() => {
this.animate(angle - 5)
})
}
// 回弹
springBack() {
console.log('旋转动画结束')
console.log(`打印要结束的旋转动画 id: ${timer}`)
cancelFrame(timer)
this.state.y = 0
const rect = this.refresherRef.getBoundingClientRect()
console.log('回弹动画开始')
slideTo(this.refresherRef, rect.top, () => {
this.isSpringBack = true
this.state.isRunning = false
})
this.state.rate = 1
this.state.lastY = 0
}
move(touch) {
const dy = touch.clientY - this.state.lastY
this.state.lastY = touch.clientY
this.state.rate = this.getRate(dy, this.state.rate)
this.state.y += dy * this.state.rate
if (this.state.y >= masDragDistance) {
this.state.y = masDragDistance
}
if (this.state.y < 0) {
this.state.y = 0
}
this.dragMove(this.state.y)
}
// 手势
getRate(distance, rate) {
this.state.ladderDistance += distance
if (distance > 0) {
if (this.state.ladderDistance >= 100) {
rate = Math.max(0, rate - 0.1)
this.state.ladderDistance = 0
}
} else {
if (this.state.ladderDistance < -100) {
rate = Math.min(1, rate + 0.1)
this.state.ladderDistance = 0
}
}
return rate
}
dragMove(translateY) {
if (!this.refresherRef)
return
const angle = ~~(translateY / masDragDistance * 360) * -1
this.refresherRef.style.transform = `translate(0px, ${translateY}px) rotate(${angle}deg)`
this.onPulling && this.onPulling(translateY)
}
touchStart(e) {
const touch = e.touches[0]
this.prepare(touch)
this.isSpringBack = false
this.onPullStart && this.onPullStart()
}
touchEnd(e) {
if (this.state.y >= masDragDistance) {
console.log('触发旋转动画')
this.rotate(0)
this.onReleaseToRefresh && this.onReleaseToRefresh()
} else {
console.log('触发回弹动画')
this.springBack()
}
this.destroy()
}
touchMove(e) {
const touch = e.touches[0]
this.move(touch)
}
}