UNPKG

react-tappable

Version:

Touch / Tappable Event Handling Component for React

105 lines (86 loc) 4.1 kB
'use strict'; var PropTypes = require('prop-types'); var React = require('react'); function getPinchProps(touches) { return { touches: Array.prototype.map.call(touches, function copyTouch(touch) { return { identifier: touch.identifier, pageX: touch.pageX, pageY: touch.pageY }; }), center: { x: (touches[0].pageX + touches[1].pageX) / 2, y: (touches[0].pageY + touches[1].pageY) / 2 }, angle: Math.atan() * (touches[1].pageY - touches[0].pageY) / (touches[1].pageX - touches[0].pageX) * 180 / Math.PI, distance: Math.sqrt(Math.pow(Math.abs(touches[1].pageX - touches[0].pageX), 2) + Math.pow(Math.abs(touches[1].pageY - touches[0].pageY), 2)) }; } var Mixin = { propTypes: { onPinchStart: PropTypes.func, // fires when a pinch gesture is started onPinchMove: PropTypes.func, // fires on every touch-move when a pinch action is active onPinchEnd: PropTypes.func // fires when a pinch action ends }, onPinchStart: function onPinchStart(event) { // in case the two touches didn't start exactly at the same time if (this._initialTouch) { this.endTouch(); } var touches = event.touches; this._initialPinch = getPinchProps(touches); this._initialPinch = Object.assign(this._initialPinch, { displacement: { x: 0, y: 0 }, displacementVelocity: { x: 0, y: 0 }, rotation: 0, rotationVelocity: 0, zoom: 1, zoomVelocity: 0, time: Date.now() }); this._lastPinch = this._initialPinch; this.props.onPinchStart && this.props.onPinchStart(this._initialPinch, event); }, onPinchMove: function onPinchMove(event) { if (this._initialTouch) { this.endTouch(); } var touches = event.touches; if (touches.length !== 2) { return this.onPinchEnd(event); // bail out before disaster } var currentPinch = touches[0].identifier === this._initialPinch.touches[0].identifier && touches[1].identifier === this._initialPinch.touches[1].identifier ? getPinchProps(touches) // the touches are in the correct order : touches[1].identifier === this._initialPinch.touches[0].identifier && touches[0].identifier === this._initialPinch.touches[1].identifier ? getPinchProps(touches.reverse()) // the touches have somehow changed order : getPinchProps(touches); // something is wrong, but we still have two touch-points, so we try not to fail currentPinch.displacement = { x: currentPinch.center.x - this._initialPinch.center.x, y: currentPinch.center.y - this._initialPinch.center.y }; currentPinch.time = Date.now(); var timeSinceLastPinch = currentPinch.time - this._lastPinch.time; currentPinch.displacementVelocity = { x: (currentPinch.displacement.x - this._lastPinch.displacement.x) / timeSinceLastPinch, y: (currentPinch.displacement.y - this._lastPinch.displacement.y) / timeSinceLastPinch }; currentPinch.rotation = currentPinch.angle - this._initialPinch.angle; currentPinch.rotationVelocity = currentPinch.rotation - this._lastPinch.rotation / timeSinceLastPinch; currentPinch.zoom = currentPinch.distance / this._initialPinch.distance; currentPinch.zoomVelocity = (currentPinch.zoom - this._lastPinch.zoom) / timeSinceLastPinch; this.props.onPinchMove && this.props.onPinchMove(currentPinch, event); this._lastPinch = currentPinch; }, onPinchEnd: function onPinchEnd(event) { // TODO use helper to order touches by identifier and use actual values on touchEnd. var currentPinch = Object.assign({}, this._lastPinch); currentPinch.time = Date.now(); if (currentPinch.time - this._lastPinch.time > 16) { currentPinch.displacementVelocity = 0; currentPinch.rotationVelocity = 0; currentPinch.zoomVelocity = 0; } this.props.onPinchEnd && this.props.onPinchEnd(currentPinch, event); this._initialPinch = this._lastPinch = null; // If one finger is still on screen, it should start a new touch event for swiping etc // But it should never fire an onTap or onPress event. // Since there is no support swipes yet, this should be disregarded for now // if (event.touches.length === 1) { // this.onTouchStart(event); // } } }; module.exports = Mixin;