UNPKG

sigma

Version:

A JavaScript library dedicated to graph drawing.

245 lines (244 loc) 11.4 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var captor_1 = __importStar(require("./captor")); var camera_1 = __importDefault(require("../camera")); var DRAG_TIMEOUT = 200; var TOUCH_INERTIA_RATIO = 3; var TOUCH_INERTIA_DURATION = 200; /** * Touch captor class. * * @constructor */ var TouchCaptor = /** @class */ (function (_super) { __extends(TouchCaptor, _super); function TouchCaptor(container, camera) { var _this = _super.call(this, container, camera) || this; _this.enabled = true; _this.isMoving = false; _this.touchMode = 0; // number of touches down // Binding methods: _this.handleStart = _this.handleStart.bind(_this); _this.handleLeave = _this.handleLeave.bind(_this); _this.handleMove = _this.handleMove.bind(_this); // Binding events container.addEventListener("touchstart", _this.handleStart, false); container.addEventListener("touchend", _this.handleLeave, false); container.addEventListener("touchcancel", _this.handleLeave, false); container.addEventListener("touchmove", _this.handleMove, false); return _this; } TouchCaptor.prototype.kill = function () { var container = this.container; container.removeEventListener("touchstart", this.handleStart); container.removeEventListener("touchend", this.handleLeave); container.removeEventListener("touchcancel", this.handleLeave); container.removeEventListener("touchmove", this.handleMove); }; TouchCaptor.prototype.getDimensions = function () { return { width: this.container.offsetWidth, height: this.container.offsetHeight, }; }; TouchCaptor.prototype.dispatchRelatedMouseEvent = function (type, e, position, emitter) { var mousePosition = position || captor_1.getPosition(e.touches[0]); var mouseEvent = new MouseEvent(type, { clientX: mousePosition.x, clientY: mousePosition.y, altKey: e.altKey, ctrlKey: e.ctrlKey, }); (emitter || this.container).dispatchEvent(mouseEvent); }; TouchCaptor.prototype.handleStart = function (e) { if (!this.enabled) return; // Prevent default to avoid default browser behaviors... e.preventDefault(); // ...but simulate mouse behavior anyway, to get the MouseCaptor working as well: if (e.touches.length === 1) this.dispatchRelatedMouseEvent("mousedown", e); var touches = captor_1.getTouchesArray(e.touches); this.isMoving = true; this.touchMode = touches.length; this.startCameraState = this.camera.getState(); this.startTouchesPositions = touches.map(captor_1.getPosition); this.lastTouchesPositions = this.startTouchesPositions; // When there are two touches down, let's record distance and angle as well: if (this.touchMode === 2) { var _a = __read(this.startTouchesPositions, 2), _b = _a[0], x0 = _b.x, y0 = _b.y, _c = _a[1], x1 = _c.x, y1 = _c.y; this.startTouchesAngle = Math.atan2(y1 - y0, x1 - x0); this.startTouchesDistance = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2)); } this.emit("touchdown", captor_1.getTouchCoords(e)); }; TouchCaptor.prototype.handleLeave = function (e) { if (!this.enabled) return; // Prevent default to avoid default browser behaviors... e.preventDefault(); // ...but simulate mouse behavior anyway, to get the MouseCaptor working as well: if (e.touches.length === 0 && this.lastTouchesPositions && this.lastTouchesPositions.length) { this.dispatchRelatedMouseEvent("mouseup", e, this.lastTouchesPositions[0], document); this.dispatchRelatedMouseEvent("click", e, this.lastTouchesPositions[0]); } if (this.movingTimeout) { this.isMoving = false; clearTimeout(this.movingTimeout); } switch (this.touchMode) { case 2: if (e.touches.length === 1) { this.handleStart(e); e.preventDefault(); break; } /* falls through */ case 1: // TODO // Dispatch event if (this.isMoving) { var cameraState = this.camera.getState(), previousCameraState = this.camera.getPreviousState(); this.camera.animate({ x: cameraState.x + TOUCH_INERTIA_RATIO * (cameraState.x - previousCameraState.x), y: cameraState.y + TOUCH_INERTIA_RATIO * (cameraState.y - previousCameraState.y), }, { duration: TOUCH_INERTIA_DURATION, easing: "quadraticOut", }); } this.isMoving = false; this.touchMode = 0; break; } this.emit("touchup", captor_1.getTouchCoords(e)); }; TouchCaptor.prototype.handleMove = function (e) { var _a; var _this = this; if (!this.enabled) return; // Prevent default to avoid default browser behaviors... e.preventDefault(); // ...but simulate mouse behavior anyway, to get the MouseCaptor working as well: if (e.touches.length === 1) this.dispatchRelatedMouseEvent("mousemove", e); var startCameraState = this.startCameraState; var touches = captor_1.getTouchesArray(e.touches); var touchesPositions = touches.map(captor_1.getPosition); this.lastTouchesPositions = touchesPositions; this.isMoving = true; if (this.movingTimeout) clearTimeout(this.movingTimeout); this.movingTimeout = window.setTimeout(function () { _this.isMoving = false; }, DRAG_TIMEOUT); switch (this.touchMode) { case 1: { var _b = this.camera.viewportToFramedGraph(this.getDimensions(), (this.startTouchesPositions || [])[0]), xStart = _b.x, yStart = _b.y; var _c = this.camera.viewportToFramedGraph(this.getDimensions(), touchesPositions[0]), x = _c.x, y = _c.y; this.camera.setState({ x: startCameraState.x + xStart - x, y: startCameraState.y + yStart - y, }); break; } case 2: { /** * Here is the thinking here: * * 1. We can find the new angle and ratio, by comparing the vector from "touch one" to "touch two" at the start * of the d'n'd and now * * 2. We can use `Camera#viewportToGraph` inside formula to retrieve the new camera position, using the graph * position of a touch at the beginning of the d'n'd (using `startCamera.viewportToGraph`) and the viewport * position of this same touch now */ var newCameraState = {}; var _d = touchesPositions[0], x0 = _d.x, y0 = _d.y; var _e = touchesPositions[1], x1 = _e.x, y1 = _e.y; var angleDiff = Math.atan2(y1 - y0, x1 - x0) - this.startTouchesAngle; var ratioDiff = Math.hypot(y1 - y0, x1 - x0) / this.startTouchesDistance; // 1. newCameraState.ratio = startCameraState.ratio / ratioDiff; newCameraState.angle = startCameraState.angle + angleDiff; // 2. var dimensions = this.getDimensions(); var touchGraphPosition = camera_1.default.from(startCameraState).viewportToFramedGraph(dimensions, (this.startTouchesPositions || [])[0]); var smallestDimension = Math.min(dimensions.width, dimensions.height); var dx = smallestDimension / dimensions.width; var dy = smallestDimension / dimensions.height; var ratio = newCameraState.ratio / smallestDimension; // Align with center of the graph: var x = x0 - smallestDimension / 2 / dx; var y = y0 - smallestDimension / 2 / dy; // Rotate: _a = __read([ x * Math.cos(-newCameraState.angle) - y * Math.sin(-newCameraState.angle), y * Math.cos(-newCameraState.angle) + x * Math.sin(-newCameraState.angle), ], 2), x = _a[0], y = _a[1]; newCameraState.x = touchGraphPosition.x - x * ratio; newCameraState.y = touchGraphPosition.y + y * ratio; this.camera.setState(newCameraState); break; } } this.emit("touchmove", captor_1.getTouchCoords(e)); }; return TouchCaptor; }(captor_1.default)); exports.default = TouchCaptor;