UNPKG

react-flexigrid

Version:

A React table component designed to allow presenting millions of rows of data.

208 lines (165 loc) 6.29 kB
import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import { requestAnimationFrame, getJudgeFunction } from '../utils'; var MOVE_AMPLITUDE = 1.6; var DECELERATION_AMPLITUDE = 1.6; var DECELERATION_FACTOR = 325; var TRACKER_TIMEOUT = 100; var TouchHandler = function () { function TouchHandler(onTouchScroll, handleScrollX, handleScrollY, stopPropagation) { var _this = this; _classCallCheck(this, TouchHandler); this.didTouchMove = function () { // Fire scroll callback based on computed drag delta. // Also track accummulated delta so we can calculate velocity _this.dragAnimationId = null; _this.onTouchScrollCallback(_this.deltaX, _this.deltaY); _this.accumulatedDeltaX += _this.deltaX; _this.accumulatedDeltaY += _this.deltaY; _this.deltaX = 0; _this.deltaY = 0; }; this.track = function () { // Compute velocity based on a weighted average of drag over // last 100ms and previous velocity. Combining into a moving average // results in a smoother scroll. var now = Date.now(); var elapsed = now - _this.lastFrameTimestamp; var oldVelocityX = _this.velocityX; var oldVelocityY = _this.velocityY; // We compute velocity using a weighted average of the current // velocity and the previous velocity. If the previous velocity // is 0, put the full weight on the last 100ms var weight = 0.8; if (elapsed < TRACKER_TIMEOUT) { weight *= elapsed / TRACKER_TIMEOUT; } if (oldVelocityX === 0 && oldVelocityY === 0) { weight = 1; } // Formula for computing weighted average of velocity _this.velocityX = weight * (TRACKER_TIMEOUT * _this.accumulatedDeltaX / (1 + elapsed)); if (weight < 1) { _this.velocityX += (1 - weight) * oldVelocityX; } _this.velocityY = weight * (TRACKER_TIMEOUT * _this.accumulatedDeltaY / (1 + elapsed)); if (weight < 1) { _this.velocityY += (1 - weight) * oldVelocityY; } _this.accumulatedDeltaX = 0; _this.accumulatedDeltaY = 0; _this.lastFrameTimestamp = now; }; this.startAutoScroll = function () { // To kick off deceleration / momentum scrolling, handle any // scrolling from a drag which was waiting for an animation // frame. Then update our velocity. // Finally start the momentum scrolling handler (autoScroll) _this.autoScrollTimestamp = Date.now(); if (_this.deltaX > 0 || _this.deltaY > 0) { _this.didTouchMove(); } _this.track(); _this.autoScroll(); }; this.autoScroll = function () { // Compute a scroll delta with an exponential decay based on // time elapsed since drag was released. This is called // recursively on animation frames until the delta is below // a threshold (5 pixels) var elapsed = Date.now() - _this.autoScrollTimestamp; var factor = DECELERATION_AMPLITUDE * Math.exp(-elapsed / DECELERATION_FACTOR); var deltaX = factor * _this.velocityX; var deltaY = factor * _this.velocityY; if (Math.abs(deltaX) <= 5 || !_this.handleScrollX(deltaX, deltaY)) { deltaX = 0; } if (Math.abs(deltaY) <= 5 || !_this.handleScrollY(deltaY, deltaX)) { deltaY = 0; } if (deltaX !== 0 || deltaY !== 0) { _this.onTouchScrollCallback(deltaX, deltaY); requestAnimationFrame(_this.autoScroll); } }; this.trackerId = null; this.dragAnimationId = null; this.deltaX = 0; this.deltaY = 0; this.lastTouchX = 0; this.lastTouchY = 0; this.velocityX = 0; this.velocityY = 0; this.accumulatedDeltaX = 0; this.accumulatedDeltaY = 0; this.lastFrameTimestamp = Date.now(); this.autoScrollTimestamp = Date.now(); this.handleScrollX = getJudgeFunction(handleScrollX); this.handleScrollY = getJudgeFunction(handleScrollY); this.stopPropagation = getJudgeFunction(stopPropagation); this.onTouchScrollCallback = onTouchScroll; } TouchHandler.prototype.onTouchStart = function onTouchStart(e) { this.lastTouchX = e.touches[0].pageX; this.lastTouchY = e.touches[0].pageY; this.velocityX = 0; this.velocityY = 0; this.accumulatedDeltaX = 0; this.accumulatedDeltaY = 0; this.lastFrameTimestamp = Date.now(); clearInterval(this.trackerId); this.trackerId = setInterval(this.track, TRACKER_TIMEOUT); if (this.stopPropagation()) { e.stopPropagation(); } }; TouchHandler.prototype.onTouchEnd = function onTouchEnd(e) { this.onTouchCancel(e); requestAnimationFrame(this.startAutoScroll); }; TouchHandler.prototype.onTouchCancel = function onTouchCancel(e) { clearInterval(this.trackerId); this.trackerId = null; if (this.stopPropagation()) { e.stopPropagation(); } }; TouchHandler.prototype.onTouchMove = function onTouchMove(e) { var moveX = e.touches[0].pageX; var moveY = e.touches[0].pageY; // Compute delta scrolled since last drag // Mobile, scrolling is inverted this.deltaX = MOVE_AMPLITUDE * (this.lastTouchX - moveX); this.deltaY = MOVE_AMPLITUDE * (this.lastTouchY - moveY); var handleScrollX = this.handleScrollX(this.deltaX, this.deltaY); var handleScrollY = this.handleScrollY(this.deltaY, this.deltaX); if (!handleScrollX && !handleScrollY) { return; } // If we can handle scroll update last touch for computing delta if (handleScrollX) { this.lastTouchX = moveX; } else { this.deltaX = 0; } if (handleScrollY) { this.lastTouchY = moveY; } else { this.deltaY = 0; } e.preventDefault(); // ensure minimum delta magnitude is met to avoid jitter var changed = false; if (Math.abs(this.deltaX) > 2 || Math.abs(this.deltaY) > 2) { if (this.stopPropagation()) { e.stopPropagation(); } changed = true; } // Request animation frame to trigger scroll of computed delta if (changed === true && this.dragAnimationId === null) { this.dragAnimationId = requestAnimationFrame(this.didTouchMove); } }; return TouchHandler; }(); export default TouchHandler;