UNPKG

tqpulldownrefresh

Version:
218 lines (173 loc) 6.17 kB
let lasttime = 0 const nextFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { let curtime = +new Date, delay = Math.max(1000 / 60, 1000 / 60 - (curtime - lasttime)); lasttime = curtime + delay return setTimeout(callback, delay) } const cancelFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || clearTimeout; // 动画函数 const tween = { linear: function (t, b, c, d) { return c * t / d + b; }, ease: function (t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }, 'ease-in': function (t, b, c, d) { return c * (t /= d) * t * t + b; }, 'ease-out': function (t, b, c, d) { return c * ((t = t / d - 1) * t * t + 1) + b; }, 'ease-in-out': function (t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t + b; return c / 2 * ((t -= 2) * t * t + 2) + b; }, bounce: function (t, b, c, d) { if ((t /= d) < (1 / 2.75)) { return c * (7.5625 * t * t) + b; } else if (t < (2 / 2.75)) { return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b; } else if (t < (2.5 / 2.75)) { return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b; } else { return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b; } } } // target 为目标距离 function slideTo(el, target) { // 动画持续时间 const duration = 1000 let timer = null const stime = Date.now() cancelFrame(timer) ani() function ani() { const offset = Math.min(duration, Date.now() - stime) const s = tween.ease(offset, 0, 1, duration) if (offset < duration) { el.style.transform = `translate(0px, ${(1 - s) * target}px)` timer = nextFrame(ani) } } } const masDragDistance = 300 // 最大拖动距离 class TQPullDownRefresh { state = { x: 0, y: 0, endY: 0, ladderDistance: 0, // 阶梯下降时阶梯的数值 lastY: 0, rate: 1, isRunning: false } containerRef = null areaRect = null refresherRef = null refresherRect = null onDragStart = null onDragEnd = null onDragMove = null constructor(onDragStart, onDragEnd, onDragMove) { this.onDragStart = onDragStart this.onDragEnd = onDragEnd this.onDragMove = onDragMove // 手动创建 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 = options.containerRef || document.getElementById(options.containerId) // this.areaRect = this.containerRef.getBoundingClientRect() // this.refresherRef = options.refresherRef || document.getElementById(this.options.refresherId) // this.refresherRect = this.refresherRef.getBoundingClientRect() 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 } deinit() { if (this.state.y >= masDragDistance) { this.rotate(0) } else { this.springBack() } this.containerRef.removeEventListener('touchstart', this.touchStart) this.containerRef.removeEventListener('touchmove', this.touchMove) this.containerRef.removeEventListener('touchend', this.touchEnd) } rotate(angle) { this.refresherRef.style.transform = `translate(0px, ${this.state.y}px) rotate(${angle}deg)` if (angle === -360) { angle = 0 } let timer = null cancelFrame(timer) if (this.state.isRunning) { timer = nextFrame(() => { this.rotate(angle - 5) }) } } 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.onDragMove && this.onDragMove(translateY) } touchStart(e) { this.state.isRunning = true const touch = e.touches[0] this.prepare(touch) this.onDragStart && this.onDragStart() } touchEnd(e) { this.deinit() this.onDragEnd && this.onDragEnd() } // 回弹 springBack() { this.state.isRunning = false const rect = this.refresherRef.getBoundingClientRect() slideTo(this.refresherRef, rect.top) this.state.rate = 1 this.state.lastY = 0 } touchMove(e) { const touch = e.touches[0] this.move(touch) } }