UNPKG

tqpulldownrefresh

Version:
175 lines (134 loc) 4.13 kB
import { nextFrame, cancelFrame, slideTo } from './animate.js' const masDragDistance = 300 // 最大拖动距离 const map = new Map() let timer = null export default class TQPullDownRefresh { constructor(options) { this.state = { x: 0, y: 0, endY: 0, ladderDistance: 0, lastY: 0, rate: 1, isRunning: false, } this.isSpringBack = false this.containerRef = null this.areaRect = null this.refresherRef = null this.refresherRect = null this.onPullStart = null this.onReleaseToRefresh = null this.onPulling = null this.containerRef = options.containerRef this.areaRect = this.containerRef.getBoundingClientRect() this.refresherRef = options.refresherRef this.refresherRect = this.refresherRef.getBoundingClientRect() // 避免重复引用 if (map.get(options.containerRef)) { return } map.set(options.containerRef, true) 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) } }