sigma
Version:
A JavaScript library aimed at visualizing graphs of thousands of nodes and edges.
316 lines (315 loc) • 10.5 kB
JavaScript
"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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Sigma.js Camera Class
* ======================
*
* Class designed to store camera information & used to update it.
* @module
*/
var animate_1 = require("../utils/animate");
var easings_1 = __importDefault(require("../utils/easings"));
var utils_1 = require("../utils");
var types_1 = require("../types");
/**
* Defaults.
*/
var DEFAULT_ZOOMING_RATIO = 1.5;
/**
* Camera class
*
* @constructor
*/
var Camera = /** @class */ (function (_super) {
__extends(Camera, _super);
function Camera() {
var _this = _super.call(this) || this;
_this.x = 0.5;
_this.y = 0.5;
_this.angle = 0;
_this.ratio = 1;
_this.minRatio = null;
_this.maxRatio = null;
_this.nextFrame = null;
_this.previousState = null;
_this.enabled = true;
// State
_this.previousState = _this.getState();
return _this;
}
/**
* Static method used to create a Camera object with a given state.
*
* @param state
* @return {Camera}
*/
Camera.from = function (state) {
var camera = new Camera();
return camera.setState(state);
};
/**
* Method used to enable the camera.
*
* @return {Camera}
*/
Camera.prototype.enable = function () {
this.enabled = true;
return this;
};
/**
* Method used to disable the camera.
*
* @return {Camera}
*/
Camera.prototype.disable = function () {
this.enabled = false;
return this;
};
/**
* Method used to retrieve the camera's current state.
*
* @return {object}
*/
Camera.prototype.getState = function () {
return {
x: this.x,
y: this.y,
angle: this.angle,
ratio: this.ratio,
};
};
/**
* Method used to check whether the camera has the given state.
*
* @return {object}
*/
Camera.prototype.hasState = function (state) {
return this.x === state.x && this.y === state.y && this.ratio === state.ratio && this.angle === state.angle;
};
/**
* Method used to retrieve the camera's previous state.
*
* @return {object}
*/
Camera.prototype.getPreviousState = function () {
var state = this.previousState;
if (!state)
return null;
return {
x: state.x,
y: state.y,
angle: state.angle,
ratio: state.ratio,
};
};
/**
* Method used to check minRatio and maxRatio values.
*
* @param ratio
* @return {number}
*/
Camera.prototype.getBoundedRatio = function (ratio) {
var r = ratio;
if (typeof this.minRatio === "number")
r = Math.max(r, this.minRatio);
if (typeof this.maxRatio === "number")
r = Math.min(r, this.maxRatio);
return r;
};
/**
* Method used to check various things to return a legit state candidate.
*
* @param state
* @return {object}
*/
Camera.prototype.validateState = function (state) {
var validatedState = {};
if (typeof state.x === "number")
validatedState.x = state.x;
if (typeof state.y === "number")
validatedState.y = state.y;
if (typeof state.angle === "number")
validatedState.angle = state.angle;
if (typeof state.ratio === "number")
validatedState.ratio = this.getBoundedRatio(state.ratio);
return validatedState;
};
/**
* Method used to check whether the camera is currently being animated.
*
* @return {boolean}
*/
Camera.prototype.isAnimated = function () {
return !!this.nextFrame;
};
/**
* Method used to set the camera's state.
*
* @param {object} state - New state.
* @return {Camera}
*/
Camera.prototype.setState = function (state) {
if (!this.enabled)
return this;
// TODO: update by function
// Keeping track of last state
this.previousState = this.getState();
var validState = this.validateState(state);
if (typeof validState.x === "number")
this.x = validState.x;
if (typeof validState.y === "number")
this.y = validState.y;
if (typeof validState.angle === "number")
this.angle = validState.angle;
if (typeof validState.ratio === "number")
this.ratio = validState.ratio;
// Emitting
if (!this.hasState(this.previousState))
this.emit("updated", this.getState());
return this;
};
/**
* Method used to update the camera's state using a function.
*
* @param {function} updater - Updated function taking current state and
* returning next state.
* @return {Camera}
*/
Camera.prototype.updateState = function (updater) {
this.setState(updater(this.getState()));
return this;
};
/**
* Method used to animate the camera.
*
* @param {object} state - State to reach eventually.
* @param {object} opts - Options:
* @param {number} duration - Duration of the animation.
* @param {string | number => number} easing - Easing function or name of an existing one
* @param {function} callback - Callback
*/
Camera.prototype.animate = function (state, opts, callback) {
var _this = this;
if (!this.enabled)
return;
var options = Object.assign({}, animate_1.ANIMATE_DEFAULTS, opts);
var validState = this.validateState(state);
var easing = typeof options.easing === "function" ? options.easing : easings_1.default[options.easing];
// State
var start = Date.now(), initialState = this.getState();
// Function performing the animation
var fn = function () {
var t = (Date.now() - start) / options.duration;
// The animation is over:
if (t >= 1) {
_this.nextFrame = null;
_this.setState(validState);
if (_this.animationCallback) {
_this.animationCallback.call(null);
_this.animationCallback = undefined;
}
return;
}
var coefficient = easing(t);
var newState = {};
if (typeof validState.x === "number")
newState.x = initialState.x + (validState.x - initialState.x) * coefficient;
if (typeof validState.y === "number")
newState.y = initialState.y + (validState.y - initialState.y) * coefficient;
if (typeof validState.angle === "number")
newState.angle = initialState.angle + (validState.angle - initialState.angle) * coefficient;
if (typeof validState.ratio === "number")
newState.ratio = initialState.ratio + (validState.ratio - initialState.ratio) * coefficient;
_this.setState(newState);
_this.nextFrame = (0, utils_1.requestFrame)(fn);
};
if (this.nextFrame) {
(0, utils_1.cancelFrame)(this.nextFrame);
if (this.animationCallback)
this.animationCallback.call(null);
this.nextFrame = (0, utils_1.requestFrame)(fn);
}
else {
fn();
}
this.animationCallback = callback;
};
/**
* Method used to zoom the camera.
*
* @param {number|object} factorOrOptions - Factor or options.
* @return {function}
*/
Camera.prototype.animatedZoom = function (factorOrOptions) {
if (!factorOrOptions) {
this.animate({ ratio: this.ratio / DEFAULT_ZOOMING_RATIO });
}
else {
if (typeof factorOrOptions === "number")
return this.animate({ ratio: this.ratio / factorOrOptions });
else
this.animate({
ratio: this.ratio / (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO),
}, factorOrOptions);
}
};
/**
* Method used to unzoom the camera.
*
* @param {number|object} factorOrOptions - Factor or options.
*/
Camera.prototype.animatedUnzoom = function (factorOrOptions) {
if (!factorOrOptions) {
this.animate({ ratio: this.ratio * DEFAULT_ZOOMING_RATIO });
}
else {
if (typeof factorOrOptions === "number")
return this.animate({ ratio: this.ratio * factorOrOptions });
else
this.animate({
ratio: this.ratio * (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO),
}, factorOrOptions);
}
};
/**
* Method used to reset the camera.
*
* @param {object} options - Options.
*/
Camera.prototype.animatedReset = function (options) {
this.animate({
x: 0.5,
y: 0.5,
ratio: 1,
angle: 0,
}, options);
};
/**
* Returns a new Camera instance, with the same state as the current camera.
*
* @return {Camera}
*/
Camera.prototype.copy = function () {
return Camera.from(this.getState());
};
return Camera;
}(types_1.TypedEventEmitter));
exports.default = Camera;