UNPKG

10 kBJavaScriptView Raw
1"use strict";
2var __extends = (this && this.__extends) || (function () {
3 var extendStatics = function (d, b) {
4 extendStatics = Object.setPrototypeOf ||
5 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
7 return extendStatics(d, b);
8 };
9 return function (d, b) {
10 extendStatics(d, b);
11 function __() { this.constructor = d; }
12 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13 };
14})();
15exports.__esModule = true;
16/**
17 * Sigma.js Camera Class
18 * ======================
19 *
20 * Class designed to store camera information & used to update it.
21 */
22var events_1 = require("events");
23var easings = require("./easings");
24var utils_1 = require("./utils");
25/**
26 * Defaults.
27 */
28var ANIMATE_DEFAULTS = {
29 easing: "quadraticInOut",
30 duration: 150
31};
32var DEFAULT_ZOOMING_RATIO = 1.5;
33/**
34 * Camera class
35 *
36 * @constructor
37 */
38var Camera = /** @class */ (function (_super) {
39 __extends(Camera, _super);
40 function Camera() {
41 var _this = _super.call(this) || this;
42 _this.x = 0.5;
43 _this.y = 0.5;
44 _this.angle = 0;
45 _this.ratio = 1;
46 _this.nextFrame = null;
47 _this.enabled = true;
48 // State
49 _this.previousState = _this.getState();
50 return _this;
51 }
52 /**
53 * Method used to enable the camera.
54 *
55 * @return {Camera}
56 */
57 Camera.prototype.enable = function () {
58 this.enabled = true;
59 return this;
60 };
61 /**
62 * Method used to disable the camera.
63 *
64 * @return {Camera}
65 */
66 Camera.prototype.disable = function () {
67 this.enabled = false;
68 return this;
69 };
70 /**
71 * Method used to retrieve the camera's current state.
72 *
73 * @return {object}
74 */
75 Camera.prototype.getState = function () {
76 return {
77 x: this.x,
78 y: this.y,
79 angle: this.angle,
80 ratio: this.ratio
81 };
82 };
83 /**
84 * Method used to retrieve the camera's previous state.
85 *
86 * @return {object}
87 */
88 Camera.prototype.getPreviousState = function () {
89 var state = this.previousState;
90 return {
91 x: state.x,
92 y: state.y,
93 angle: state.angle,
94 ratio: state.ratio
95 };
96 };
97 /**
98 * Method used to check whether the camera is currently being animated.
99 *
100 * @return {boolean}
101 */
102 Camera.prototype.isAnimated = function () {
103 return !!this.nextFrame;
104 };
105 /**
106 * Method returning the coordinates of a point from the graph frame to the
107 * viewport.
108 *
109 * @param {object} dimensions - Dimensions of the viewport.
110 * @param {number} x - The X coordinate.
111 * @param {number} y - The Y coordinate.
112 * @return {object} - The point coordinates in the viewport.
113 */
114 // TODO: assign to gain one object
115 // TODO: angles
116 Camera.prototype.graphToViewport = function (dimensions, x, y) {
117 var smallestDimension = Math.min(dimensions.width, dimensions.height);
118 var dx = smallestDimension / dimensions.width, dy = smallestDimension / dimensions.height;
119 // TODO: we keep on the upper left corner!
120 // TODO: how to normalize sizes?
121 return {
122 x: (x - this.x + this.ratio / 2 / dx) * (smallestDimension / this.ratio),
123 y: (this.y - y + this.ratio / 2 / dy) * (smallestDimension / this.ratio)
124 };
125 };
126 /**
127 * Method returning the coordinates of a point from the viewport frame to the
128 * graph frame.
129 *
130 * @param {object} dimensions - Dimensions of the viewport.
131 * @param {number} x - The X coordinate.
132 * @param {number} y - The Y coordinate.
133 * @return {object} - The point coordinates in the graph frame.
134 */
135 // TODO: angles
136 Camera.prototype.viewportToGraph = function (dimensions, x, y) {
137 var smallestDimension = Math.min(dimensions.width, dimensions.height);
138 var dx = smallestDimension / dimensions.width, dy = smallestDimension / dimensions.height;
139 return {
140 x: (this.ratio / smallestDimension) * x + this.x - this.ratio / 2 / dx,
141 y: -((this.ratio / smallestDimension) * y - this.y - this.ratio / 2 / dy)
142 };
143 };
144 /**
145 * Method returning the abstract rectangle containing the graph according
146 * to the camera's state.
147 *
148 * @return {object} - The view's rectangle.
149 */
150 // TODO: angle
151 Camera.prototype.viewRectangle = function (dimensions) {
152 // TODO: reduce relative margin?
153 var marginX = (0 * dimensions.width) / 8, marginY = (0 * dimensions.height) / 8;
154 var p1 = this.viewportToGraph(dimensions, 0 - marginX, 0 - marginY), p2 = this.viewportToGraph(dimensions, dimensions.width + marginX, 0 - marginY), h = this.viewportToGraph(dimensions, 0, dimensions.height + marginY);
155 return {
156 x1: p1.x,
157 y1: p1.y,
158 x2: p2.x,
159 y2: p2.y,
160 height: p2.y - h.y
161 };
162 };
163 /**
164 * Method used to set the camera's state.
165 *
166 * @param {object} state - New state.
167 * @return {Camera}
168 */
169 Camera.prototype.setState = function (state) {
170 if (!this.enabled)
171 return this;
172 // TODO: validations
173 // TODO: update by function
174 // Keeping track of last state
175 this.previousState = this.getState();
176 if ("x" in state)
177 this.x = state.x;
178 if ("y" in state)
179 this.y = state.y;
180 if ("angle" in state)
181 this.angle = state.angle;
182 if ("ratio" in state)
183 this.ratio = state.ratio;
184 // Emitting
185 // TODO: don't emit if nothing changed?
186 this.emit("updated", this.getState());
187 return this;
188 };
189 /**
190 * Method used to animate the camera.
191 *
192 * @param {object} state - State to reach eventually.
193 * @param {object} options - Options:
194 * @param {number} duration - Duration of the animation.
195 * @param {function} callback - Callback
196 * @return {function} - Return a function to cancel the animation.
197 */
198 Camera.prototype.animate = function (state, options, callback) {
199 var _this = this;
200 if (!this.enabled)
201 return this;
202 // TODO: validation
203 options = utils_1.assign({}, ANIMATE_DEFAULTS, options);
204 var easing = typeof options.easing === "function" ? options.easing : easings[options.easing];
205 // Canceling previous animation if needed
206 if (this.nextFrame)
207 cancelAnimationFrame(this.nextFrame);
208 // State
209 var start = Date.now(), initialState = this.getState();
210 // Function performing the animation
211 var fn = function () {
212 var t = (Date.now() - start) / options.duration;
213 // The animation is over:
214 if (t >= 1) {
215 _this.nextFrame = null;
216 _this.setState(state);
217 if (typeof callback === "function")
218 callback();
219 return;
220 }
221 var coefficient = easing(t);
222 var newState = {};
223 if ("x" in state)
224 newState.x = initialState.x + (state.x - initialState.x) * coefficient;
225 if ("y" in state)
226 newState.y = initialState.y + (state.y - initialState.y) * coefficient;
227 if ("angle" in state)
228 newState.angle = initialState.angle + (state.angle - initialState.angle) * coefficient;
229 if ("ratio" in state)
230 newState.ratio = initialState.ratio + (state.ratio - initialState.ratio) * coefficient;
231 _this.setState(newState);
232 _this.nextFrame = requestAnimationFrame(fn);
233 };
234 if (this.nextFrame) {
235 cancelAnimationFrame(this.nextFrame);
236 this.nextFrame = requestAnimationFrame(fn);
237 }
238 else {
239 fn();
240 }
241 };
242 /**
243 * Method used to zoom the camera.
244 *
245 * @param {number|object} factorOrOptions - Factor or options.
246 * @return {function}
247 */
248 Camera.prototype.animatedZoom = function (factorOrOptions) {
249 if (!factorOrOptions) {
250 return this.animate({ ratio: this.ratio / DEFAULT_ZOOMING_RATIO });
251 }
252 else {
253 if (typeof factorOrOptions === "number")
254 return this.animate({ ratio: this.ratio / factorOrOptions });
255 else
256 return this.animate({
257 ratio: this.ratio / (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO)
258 }, factorOrOptions);
259 }
260 };
261 /**
262 * Method used to unzoom the camera.
263 *
264 * @param {number|object} factorOrOptions - Factor or options.
265 * @return {function}
266 */
267 Camera.prototype.animatedUnzoom = function (factorOrOptions) {
268 if (!factorOrOptions) {
269 return this.animate({ ratio: this.ratio * DEFAULT_ZOOMING_RATIO });
270 }
271 else {
272 if (typeof factorOrOptions === "number")
273 return this.animate({ ratio: this.ratio * factorOrOptions });
274 else
275 return this.animate({
276 ratio: this.ratio * (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO)
277 }, factorOrOptions);
278 }
279 };
280 /**
281 * Method used to reset the camera.
282 *
283 * @param {object} options - Options.
284 * @return {function}
285 */
286 Camera.prototype.animatedReset = function (options) {
287 return this.animate({
288 x: 0.5,
289 y: 0.5,
290 ratio: 1,
291 angle: 0
292 }, options);
293 };
294 return Camera;
295}(events_1.EventEmitter));
296exports["default"] = Camera;