force-graph 1.42.3 → 1.42.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@ force-graph
3
3
 
4
4
  [![NPM package][npm-img]][npm-url]
5
5
  [![Build Size][build-size-img]][build-size-url]
6
- [![Dependencies][dependencies-img]][dependencies-url]
6
+ [![NPM Downloads][npm-downloads-img]][npm-downloads-url]
7
7
 
8
8
  Force-directed graph rendered on HTML5 canvas.
9
9
 
@@ -15,7 +15,12 @@ A web component to represent a graph data structure in a 2-dimensional canvas us
15
15
  Uses HTML5 canvas for rendering and [d3-force](https://github.com/d3/d3-force) for the underlying physics engine.
16
16
  Supports canvas zooming/panning, node dragging and node/link hover/click interactions.
17
17
 
18
- Check out the examples:
18
+ See also the [3D version](https://github.com/vasturiano/3d-force-graph).
19
+
20
+ And check out the [React bindings](https://github.com/vasturiano/react-force-graph).
21
+
22
+ ## Examples
23
+
19
24
  * [Basic](https://vasturiano.github.io/force-graph/example/basic/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/basic/index.html))
20
25
  * [Load JSON](https://vasturiano.github.io/force-graph/example/load-json/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/load-json/index.html))
21
26
  * [Medium size graph (~4k elements)](https://vasturiano.github.io/force-graph/example/medium-graph/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/medium-graph/index.html))
@@ -46,28 +51,24 @@ Check out the examples:
46
51
  * [yarn.lock dependency graph (DAG mode)](https://vasturiano.github.io/force-graph/example/dag-yarn/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/dag-yarn/index.html))
47
52
  * [Usage as UI to construct graphs](https://vasturiano.github.io/force-graph/example/build-a-graph/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/build-a-graph/index.html))
48
53
 
49
- See also the [3D version](https://github.com/vasturiano/3d-force-graph).
50
-
51
- And check out the [React bindings](https://github.com/vasturiano/react-force-graph).
52
-
53
54
  ## Quick start
54
55
 
55
- ```
56
+ ```js
56
57
  import ForceGraph from 'force-graph';
57
58
  ```
58
59
  or
59
- ```
60
- var ForceGraph = require('force-graph');
60
+ ```js
61
+ const ForceGraph = require('force-graph');
61
62
  ```
62
63
  or even
63
- ```
64
+ ```html
64
65
  <script src="//unpkg.com/force-graph"></script>
65
66
  ```
66
67
  then
67
- ```
68
- var myGraph = ForceGraph();
68
+ ```js
69
+ const myGraph = ForceGraph();
69
70
  myGraph(<myDOMElement>)
70
- .graphData(<myData>);
71
+ .graphData(<myData>);
71
72
  ```
72
73
 
73
74
  ## API reference
@@ -191,7 +192,7 @@ myGraph(<myDOMElement>)
191
192
  | <b>graph2ScreenCoords</b>(<i>x</i>, <i>y</i>) | Utility method to translate node coordinates to the viewport domain. Given a pair of `x`,`y` graph coordinates, returns the current equivalent `{x, y}` in viewport coordinates. |
192
193
 
193
194
  ### Input JSON syntax
194
- ```
195
+ ```json
195
196
  {
196
197
  "nodes": [
197
198
  {
@@ -204,14 +205,14 @@ myGraph(<myDOMElement>)
204
205
  "name": "name2",
205
206
  "val": 10
206
207
  },
207
- (...)
208
+ ...
208
209
  ],
209
210
  "links": [
210
211
  {
211
212
  "source": "id1",
212
213
  "target": "id2"
213
214
  },
214
- (...)
215
+ ...
215
216
  ]
216
217
  }
217
218
  ```
@@ -220,9 +221,9 @@ myGraph(<myDOMElement>)
220
221
 
221
222
  [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=L398E7PKP47E8&currency_code=USD&source=url) If this project has helped you and you'd like to contribute back, you can always [buy me a ☕](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=L398E7PKP47E8&currency_code=USD&source=url)!
222
223
 
223
- [npm-img]: https://img.shields.io/npm/v/force-graph.svg
224
+ [npm-img]: https://img.shields.io/npm/v/force-graph
224
225
  [npm-url]: https://npmjs.org/package/force-graph
225
- [build-size-img]: https://img.shields.io/bundlephobia/minzip/force-graph.svg
226
+ [build-size-img]: https://img.shields.io/bundlephobia/minzip/force-graph
226
227
  [build-size-url]: https://bundlephobia.com/result?p=force-graph
227
- [dependencies-img]: https://img.shields.io/david/vasturiano/force-graph.svg
228
- [dependencies-url]: https://david-dm.org/vasturiano/force-graph
228
+ [npm-downloads-img]: https://img.shields.io/npm/dt/force-graph
229
+ [npm-downloads-url]: https://www.npmtrends.com/force-graph
@@ -61,14 +61,9 @@ function ownKeys(object, enumerableOnly) {
61
61
 
62
62
  if (Object.getOwnPropertySymbols) {
63
63
  var symbols = Object.getOwnPropertySymbols(object);
64
-
65
- if (enumerableOnly) {
66
- symbols = symbols.filter(function (sym) {
67
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
68
- });
69
- }
70
-
71
- keys.push.apply(keys, symbols);
64
+ enumerableOnly && (symbols = symbols.filter(function (sym) {
65
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
66
+ })), keys.push.apply(keys, symbols);
72
67
  }
73
68
 
74
69
  return keys;
@@ -76,19 +71,12 @@ function ownKeys(object, enumerableOnly) {
76
71
 
77
72
  function _objectSpread2(target) {
78
73
  for (var i = 1; i < arguments.length; i++) {
79
- var source = arguments[i] != null ? arguments[i] : {};
80
-
81
- if (i % 2) {
82
- ownKeys(Object(source), true).forEach(function (key) {
83
- _defineProperty(target, key, source[key]);
84
- });
85
- } else if (Object.getOwnPropertyDescriptors) {
86
- Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
87
- } else {
88
- ownKeys(Object(source)).forEach(function (key) {
89
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
90
- });
91
- }
74
+ var source = null != arguments[i] ? arguments[i] : {};
75
+ i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
76
+ _defineProperty(target, key, source[key]);
77
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
78
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
79
+ });
92
80
  }
93
81
 
94
82
  return target;
@@ -97,17 +85,11 @@ function _objectSpread2(target) {
97
85
  function _typeof(obj) {
98
86
  "@babel/helpers - typeof";
99
87
 
100
- if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
101
- _typeof = function (obj) {
102
- return typeof obj;
103
- };
104
- } else {
105
- _typeof = function (obj) {
106
- return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
107
- };
108
- }
109
-
110
- return _typeof(obj);
88
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
89
+ return typeof obj;
90
+ } : function (obj) {
91
+ return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
92
+ }, _typeof(obj);
111
93
  }
112
94
 
113
95
  function _defineProperty(obj, key, value) {
@@ -1263,11 +1245,9 @@ var forceGraph = Kapsule__default["default"]({
1263
1245
  triggerUpdate: false
1264
1246
  },
1265
1247
  onZoom: {
1266
- "default": function _default() {},
1267
1248
  triggerUpdate: false
1268
1249
  },
1269
1250
  onZoomEnd: {
1270
- "default": function _default() {},
1271
1251
  triggerUpdate: false
1272
1252
  },
1273
1253
  onRenderFramePre: {
@@ -1455,6 +1435,8 @@ var forceGraph = Kapsule__default["default"]({
1455
1435
  };
1456
1436
  },
1457
1437
  init: function init(domNode, state) {
1438
+ var _this = this;
1439
+
1458
1440
  // Wipe DOM
1459
1441
  domNode.innerHTML = ''; // Container anchor for canvas and tooltip
1460
1442
 
@@ -1580,10 +1562,11 @@ var forceGraph = Kapsule__default["default"]({
1580
1562
  c.translate(t.x, t.y);
1581
1563
  c.scale(t.k, t.k);
1582
1564
  });
1583
- state.onZoom(_objectSpread2({}, t));
1565
+ state.onZoom && state.onZoom(_objectSpread2(_objectSpread2({}, t), _this.centerAt())); // report x,y coordinates relative to canvas center
1566
+
1584
1567
  state.needsRedraw = true;
1585
1568
  }).on('end', function (ev) {
1586
- return state.onZoomEnd(_objectSpread2({}, ev.transform));
1569
+ return state.onZoomEnd && state.onZoomEnd(_objectSpread2(_objectSpread2({}, ev.transform), _this.centerAt()));
1587
1570
  });
1588
1571
  adjustCanvasSize(state);
1589
1572
  state.forceGraph.onNeedsRedraw(function () {
@@ -30,7 +30,7 @@ type DagMode = 'td' | 'bu' | 'lr' | 'rl' | 'radialout' | 'radialin';
30
30
 
31
31
  interface ForceFn {
32
32
  (alpha: number): void;
33
- initialize?: (nodes: NodeObject[]) => void;
33
+ initialize?: (nodes: NodeObject[], ...args: any[]) => void;
34
34
  [key: string]: any;
35
35
  }
36
36
 
@@ -1,4 +1,4 @@
1
- // Version 1.42.3 force-graph - https://github.com/vasturiano/force-graph
1
+ // Version 1.42.7 force-graph - https://github.com/vasturiano/force-graph
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
4
  typeof define === 'function' && define.amd ? define(factory) :
@@ -40,14 +40,9 @@
40
40
 
41
41
  if (Object.getOwnPropertySymbols) {
42
42
  var symbols = Object.getOwnPropertySymbols(object);
43
-
44
- if (enumerableOnly) {
45
- symbols = symbols.filter(function (sym) {
46
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
47
- });
48
- }
49
-
50
- keys.push.apply(keys, symbols);
43
+ enumerableOnly && (symbols = symbols.filter(function (sym) {
44
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
45
+ })), keys.push.apply(keys, symbols);
51
46
  }
52
47
 
53
48
  return keys;
@@ -55,19 +50,12 @@
55
50
 
56
51
  function _objectSpread2(target) {
57
52
  for (var i = 1; i < arguments.length; i++) {
58
- var source = arguments[i] != null ? arguments[i] : {};
59
-
60
- if (i % 2) {
61
- ownKeys(Object(source), true).forEach(function (key) {
62
- _defineProperty(target, key, source[key]);
63
- });
64
- } else if (Object.getOwnPropertyDescriptors) {
65
- Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
66
- } else {
67
- ownKeys(Object(source)).forEach(function (key) {
68
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
69
- });
70
- }
53
+ var source = null != arguments[i] ? arguments[i] : {};
54
+ i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
55
+ _defineProperty(target, key, source[key]);
56
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
57
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
58
+ });
71
59
  }
72
60
 
73
61
  return target;
@@ -76,17 +64,11 @@
76
64
  function _typeof(obj) {
77
65
  "@babel/helpers - typeof";
78
66
 
79
- if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
80
- _typeof = function (obj) {
81
- return typeof obj;
82
- };
83
- } else {
84
- _typeof = function (obj) {
85
- return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
86
- };
87
- }
88
-
89
- return _typeof(obj);
67
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
68
+ return typeof obj;
69
+ } : function (obj) {
70
+ return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
71
+ }, _typeof(obj);
90
72
  }
91
73
 
92
74
  function _defineProperty(obj, key, value) {
@@ -5093,6 +5075,25 @@
5093
5075
  }
5094
5076
  }
5095
5077
 
5078
+ function _defineProperties$1(target, props) {
5079
+ for (var i = 0; i < props.length; i++) {
5080
+ var descriptor = props[i];
5081
+ descriptor.enumerable = descriptor.enumerable || false;
5082
+ descriptor.configurable = true;
5083
+ if ("value" in descriptor) descriptor.writable = true;
5084
+ Object.defineProperty(target, descriptor.key, descriptor);
5085
+ }
5086
+ }
5087
+
5088
+ function _createClass$1(Constructor, protoProps, staticProps) {
5089
+ if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
5090
+ if (staticProps) _defineProperties$1(Constructor, staticProps);
5091
+ Object.defineProperty(Constructor, "prototype", {
5092
+ writable: false
5093
+ });
5094
+ return Constructor;
5095
+ }
5096
+
5096
5097
  function _slicedToArray$1(arr, i) {
5097
5098
  return _arrayWithHoles$1(arr) || _iterableToArrayLimit$1(arr, i) || _unsupportedIterableToArray$2(arr, i) || _nonIterableRest$1();
5098
5099
  }
@@ -5102,7 +5103,7 @@
5102
5103
  }
5103
5104
 
5104
5105
  function _iterableToArrayLimit$1(arr, i) {
5105
- var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]);
5106
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
5106
5107
 
5107
5108
  if (_i == null) return;
5108
5109
  var _arr = [];
@@ -5152,7 +5153,7 @@
5152
5153
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
5153
5154
  }
5154
5155
 
5155
- var Prop = function Prop(name, _ref) {
5156
+ var Prop = /*#__PURE__*/_createClass$1(function Prop(name, _ref) {
5156
5157
  var _ref$default = _ref["default"],
5157
5158
  defaultVal = _ref$default === void 0 ? null : _ref$default,
5158
5159
  _ref$triggerUpdate = _ref.triggerUpdate,
@@ -5166,7 +5167,7 @@
5166
5167
  this.defaultVal = defaultVal;
5167
5168
  this.triggerUpdate = triggerUpdate;
5168
5169
  this.onChange = onChange;
5169
- };
5170
+ });
5170
5171
 
5171
5172
  function index$3 (_ref2) {
5172
5173
  var _ref2$stateInit = _ref2.stateInit,
@@ -8837,21 +8838,13 @@
8837
8838
  },
8838
8839
 
8839
8840
  makeline: function (p1, p2) {
8840
- const x1 = p1.x,
8841
- y1 = p1.y,
8842
- x2 = p2.x,
8843
- y2 = p2.y,
8844
- dx = (x2 - x1) / 3,
8845
- dy = (y2 - y1) / 3;
8846
8841
  return new Bezier(
8847
- x1,
8848
- y1,
8849
- x1 + dx,
8850
- y1 + dy,
8851
- x1 + 2 * dx,
8852
- y1 + 2 * dy,
8853
- x2,
8854
- y2
8842
+ p1.x,
8843
+ p1.y,
8844
+ (p1.x + p2.x) / 2,
8845
+ (p1.y + p2.y) / 2,
8846
+ p2.x,
8847
+ p2.y
8855
8848
  );
8856
8849
  },
8857
8850
 
@@ -9488,11 +9481,12 @@
9488
9481
  if (_3d) dims.push("z");
9489
9482
  this.dimlen = dims.length;
9490
9483
 
9484
+ // is this curve, practically speaking, a straight line?
9491
9485
  const aligned = utils.align(points, { p1: points[0], p2: points[order] });
9492
- this._linear = !aligned.some((p) => abs(p.y) > 0.0001);
9486
+ const baselength = utils.dist(points[0], points[order]);
9487
+ this._linear = aligned.reduce((t, p) => t + abs(p.y), 0) < baselength / 50;
9493
9488
 
9494
9489
  this._lut = [];
9495
-
9496
9490
  this._t1 = 0;
9497
9491
  this._t2 = 1;
9498
9492
  this.update();
@@ -9649,9 +9643,9 @@
9649
9643
  return this._lut;
9650
9644
  }
9651
9645
  this._lut = [];
9652
- // We want a range from 0 to 1 inclusive, so
9653
- // we decrement and then use <= rather than <:
9654
- steps--;
9646
+ // n steps means n+1 points
9647
+ steps++;
9648
+ this._lut = [];
9655
9649
  for (let i = 0, p, t; i < steps; i++) {
9656
9650
  t = i / (steps - 1);
9657
9651
  p = this.compute(t);
@@ -10032,6 +10026,22 @@
10032
10026
  return pass2;
10033
10027
  }
10034
10028
 
10029
+ translate(v, d1, d2) {
10030
+ d2 = typeof d2 === "number" ? d2 : d1;
10031
+
10032
+ // TODO: make this take curves with control points outside
10033
+ // of the start-end interval into account
10034
+
10035
+ const o = this.order;
10036
+ let d = this.points.map((_, i) => (1 - i / o) * d1 + (i / o) * d2);
10037
+ return new Bezier(
10038
+ this.points.map((p, i) => ({
10039
+ x: p.x + v.x * d[i],
10040
+ y: p.y + v.y * d[i],
10041
+ }))
10042
+ );
10043
+ }
10044
+
10035
10045
  scale(d) {
10036
10046
  const order = this.order;
10037
10047
  let distanceFn = false;
@@ -10042,21 +10052,31 @@
10042
10052
  return this.raise().scale(distanceFn);
10043
10053
  }
10044
10054
 
10045
- // TODO: add special handling for degenerate (=linear) curves.
10055
+ // TODO: add special handling for non-linear degenerate curves.
10056
+
10046
10057
  const clockwise = this.clockwise;
10058
+ const points = this.points;
10059
+
10060
+ if (this._linear) {
10061
+ return this.translate(
10062
+ this.normal(0),
10063
+ distanceFn ? distanceFn(0) : d,
10064
+ distanceFn ? distanceFn(1) : d
10065
+ );
10066
+ }
10067
+
10047
10068
  const r1 = distanceFn ? distanceFn(0) : d;
10048
10069
  const r2 = distanceFn ? distanceFn(1) : d;
10049
10070
  const v = [this.offset(0, 10), this.offset(1, 10)];
10050
- const points = this.points;
10051
10071
  const np = [];
10052
10072
  const o = utils.lli4(v[0], v[0].c, v[1], v[1].c);
10053
10073
 
10054
10074
  if (!o) {
10055
10075
  throw new Error("cannot scale this curve. Try reducing it first.");
10056
10076
  }
10057
- // move all points by distance 'd' wrt the origin 'o'
10058
10077
 
10059
- // move end points by fixed distance along normal.
10078
+ // move all points by distance 'd' wrt the origin 'o',
10079
+ // and move end points by fixed distance along normal.
10060
10080
  [0, 1].forEach(function (t) {
10061
10081
  const p = (np[t * order] = utils.copy(points[t * order]));
10062
10082
  p.x += (t ? r2 : r1) * v[t].n.x;
@@ -10099,7 +10119,38 @@
10099
10119
  }
10100
10120
 
10101
10121
  outline(d1, d2, d3, d4) {
10102
- d2 = typeof d2 === "undefined" ? d1 : d2;
10122
+ d2 = d2 === undefined ? d1 : d2;
10123
+
10124
+ if (this._linear) {
10125
+ // TODO: find the actual extrema, because they might
10126
+ // be before the start, or past the end.
10127
+
10128
+ const n = this.normal(0);
10129
+ const start = this.points[0];
10130
+ const end = this.points[this.points.length - 1];
10131
+ let s, mid, e;
10132
+
10133
+ if (d3 === undefined) {
10134
+ d3 = d1;
10135
+ d4 = d2;
10136
+ }
10137
+
10138
+ s = { x: start.x + n.x * d1, y: start.y + n.y * d1 };
10139
+ e = { x: end.x + n.x * d3, y: end.y + n.y * d3 };
10140
+ mid = { x: (s.x + e.x) / 2, y: (s.y + e.y) / 2 };
10141
+ const fline = [s, mid, e];
10142
+
10143
+ s = { x: start.x - n.x * d2, y: start.y - n.y * d2 };
10144
+ e = { x: end.x - n.x * d4, y: end.y - n.y * d4 };
10145
+ mid = { x: (s.x + e.x) / 2, y: (s.y + e.y) / 2 };
10146
+ const bline = [e, mid, s];
10147
+
10148
+ const ls = utils.makeline(bline[2], fline[0]);
10149
+ const le = utils.makeline(fline[2], bline[0]);
10150
+ const segments = [ls, new Bezier(fline), le, new Bezier(bline)];
10151
+ return new PolyBezier(segments);
10152
+ }
10153
+
10103
10154
  const reduced = this.reduce(),
10104
10155
  len = reduced.length,
10105
10156
  fcurves = [];
@@ -10158,7 +10209,6 @@
10158
10209
  ls = utils.makeline(bs, fs),
10159
10210
  le = utils.makeline(fe, be),
10160
10211
  segments = [ls].concat(fcurves).concat([le]).concat(bcurves);
10161
- segments.length;
10162
10212
 
10163
10213
  return new PolyBezier(segments);
10164
10214
  }
@@ -11674,11 +11724,9 @@
11674
11724
  triggerUpdate: false
11675
11725
  },
11676
11726
  onZoom: {
11677
- "default": function _default() {},
11678
11727
  triggerUpdate: false
11679
11728
  },
11680
11729
  onZoomEnd: {
11681
- "default": function _default() {},
11682
11730
  triggerUpdate: false
11683
11731
  },
11684
11732
  onRenderFramePre: {
@@ -11866,6 +11914,8 @@
11866
11914
  };
11867
11915
  },
11868
11916
  init: function init(domNode, state) {
11917
+ var _this = this;
11918
+
11869
11919
  // Wipe DOM
11870
11920
  domNode.innerHTML = ''; // Container anchor for canvas and tooltip
11871
11921
 
@@ -11991,10 +12041,11 @@
11991
12041
  c.translate(t.x, t.y);
11992
12042
  c.scale(t.k, t.k);
11993
12043
  });
11994
- state.onZoom(_objectSpread2({}, t));
12044
+ state.onZoom && state.onZoom(_objectSpread2(_objectSpread2({}, t), _this.centerAt())); // report x,y coordinates relative to canvas center
12045
+
11995
12046
  state.needsRedraw = true;
11996
12047
  }).on('end', function (ev) {
11997
- return state.onZoomEnd(_objectSpread2({}, ev.transform));
12048
+ return state.onZoomEnd && state.onZoomEnd(_objectSpread2(_objectSpread2({}, ev.transform), _this.centerAt()));
11998
12049
  });
11999
12050
  adjustCanvasSize(state);
12000
12051
  state.forceGraph.onNeedsRedraw(function () {