UNPKG

tqpulldownrefresh

Version:
180 lines (139 loc) 4.47 kB
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) } }