d3-zoom.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. // https://d3js.org/d3-zoom/ v1.8.3 Copyright 2019 Mike Bostock
  2. (function (global, factory) {
  3. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-dispatch'), require('d3-drag'), require('d3-interpolate'), require('d3-selection'), require('d3-transition')) :
  4. typeof define === 'function' && define.amd ? define(['exports', 'd3-dispatch', 'd3-drag', 'd3-interpolate', 'd3-selection', 'd3-transition'], factory) :
  5. (global = global || self, factory(global.d3 = global.d3 || {}, global.d3, global.d3, global.d3, global.d3, global.d3));
  6. }(this, function (exports, d3Dispatch, d3Drag, d3Interpolate, d3Selection, d3Transition) { 'use strict';
  7. function constant(x) {
  8. return function() {
  9. return x;
  10. };
  11. }
  12. function ZoomEvent(target, type, transform) {
  13. this.target = target;
  14. this.type = type;
  15. this.transform = transform;
  16. }
  17. function Transform(k, x, y) {
  18. this.k = k;
  19. this.x = x;
  20. this.y = y;
  21. }
  22. Transform.prototype = {
  23. constructor: Transform,
  24. scale: function(k) {
  25. return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
  26. },
  27. translate: function(x, y) {
  28. return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
  29. },
  30. apply: function(point) {
  31. return [point[0] * this.k + this.x, point[1] * this.k + this.y];
  32. },
  33. applyX: function(x) {
  34. return x * this.k + this.x;
  35. },
  36. applyY: function(y) {
  37. return y * this.k + this.y;
  38. },
  39. invert: function(location) {
  40. return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
  41. },
  42. invertX: function(x) {
  43. return (x - this.x) / this.k;
  44. },
  45. invertY: function(y) {
  46. return (y - this.y) / this.k;
  47. },
  48. rescaleX: function(x) {
  49. return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
  50. },
  51. rescaleY: function(y) {
  52. return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
  53. },
  54. toString: function() {
  55. return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
  56. }
  57. };
  58. var identity = new Transform(1, 0, 0);
  59. transform.prototype = Transform.prototype;
  60. function transform(node) {
  61. while (!node.__zoom) if (!(node = node.parentNode)) return identity;
  62. return node.__zoom;
  63. }
  64. function nopropagation() {
  65. d3Selection.event.stopImmediatePropagation();
  66. }
  67. function noevent() {
  68. d3Selection.event.preventDefault();
  69. d3Selection.event.stopImmediatePropagation();
  70. }
  71. // Ignore right-click, since that should open the context menu.
  72. function defaultFilter() {
  73. return !d3Selection.event.ctrlKey && !d3Selection.event.button;
  74. }
  75. function defaultExtent() {
  76. var e = this;
  77. if (e instanceof SVGElement) {
  78. e = e.ownerSVGElement || e;
  79. if (e.hasAttribute("viewBox")) {
  80. e = e.viewBox.baseVal;
  81. return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
  82. }
  83. return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
  84. }
  85. return [[0, 0], [e.clientWidth, e.clientHeight]];
  86. }
  87. function defaultTransform() {
  88. return this.__zoom || identity;
  89. }
  90. function defaultWheelDelta() {
  91. return -d3Selection.event.deltaY * (d3Selection.event.deltaMode === 1 ? 0.05 : d3Selection.event.deltaMode ? 1 : 0.002);
  92. }
  93. function defaultTouchable() {
  94. return navigator.maxTouchPoints || ("ontouchstart" in this);
  95. }
  96. function defaultConstrain(transform, extent, translateExtent) {
  97. var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
  98. dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
  99. dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
  100. dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
  101. return transform.translate(
  102. dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
  103. dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
  104. );
  105. }
  106. function zoom() {
  107. var filter = defaultFilter,
  108. extent = defaultExtent,
  109. constrain = defaultConstrain,
  110. wheelDelta = defaultWheelDelta,
  111. touchable = defaultTouchable,
  112. scaleExtent = [0, Infinity],
  113. translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
  114. duration = 250,
  115. interpolate = d3Interpolate.interpolateZoom,
  116. listeners = d3Dispatch.dispatch("start", "zoom", "end"),
  117. touchstarting,
  118. touchending,
  119. touchDelay = 500,
  120. wheelDelay = 150,
  121. clickDistance2 = 0;
  122. function zoom(selection) {
  123. selection
  124. .property("__zoom", defaultTransform)
  125. .on("wheel.zoom", wheeled)
  126. .on("mousedown.zoom", mousedowned)
  127. .on("dblclick.zoom", dblclicked)
  128. .filter(touchable)
  129. .on("touchstart.zoom", touchstarted)
  130. .on("touchmove.zoom", touchmoved)
  131. .on("touchend.zoom touchcancel.zoom", touchended)
  132. .style("touch-action", "none")
  133. .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
  134. }
  135. zoom.transform = function(collection, transform, point) {
  136. var selection = collection.selection ? collection.selection() : collection;
  137. selection.property("__zoom", defaultTransform);
  138. if (collection !== selection) {
  139. schedule(collection, transform, point);
  140. } else {
  141. selection.interrupt().each(function() {
  142. gesture(this, arguments)
  143. .start()
  144. .zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform)
  145. .end();
  146. });
  147. }
  148. };
  149. zoom.scaleBy = function(selection, k, p) {
  150. zoom.scaleTo(selection, function() {
  151. var k0 = this.__zoom.k,
  152. k1 = typeof k === "function" ? k.apply(this, arguments) : k;
  153. return k0 * k1;
  154. }, p);
  155. };
  156. zoom.scaleTo = function(selection, k, p) {
  157. zoom.transform(selection, function() {
  158. var e = extent.apply(this, arguments),
  159. t0 = this.__zoom,
  160. p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p,
  161. p1 = t0.invert(p0),
  162. k1 = typeof k === "function" ? k.apply(this, arguments) : k;
  163. return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
  164. }, p);
  165. };
  166. zoom.translateBy = function(selection, x, y) {
  167. zoom.transform(selection, function() {
  168. return constrain(this.__zoom.translate(
  169. typeof x === "function" ? x.apply(this, arguments) : x,
  170. typeof y === "function" ? y.apply(this, arguments) : y
  171. ), extent.apply(this, arguments), translateExtent);
  172. });
  173. };
  174. zoom.translateTo = function(selection, x, y, p) {
  175. zoom.transform(selection, function() {
  176. var e = extent.apply(this, arguments),
  177. t = this.__zoom,
  178. p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
  179. return constrain(identity.translate(p0[0], p0[1]).scale(t.k).translate(
  180. typeof x === "function" ? -x.apply(this, arguments) : -x,
  181. typeof y === "function" ? -y.apply(this, arguments) : -y
  182. ), e, translateExtent);
  183. }, p);
  184. };
  185. function scale(transform, k) {
  186. k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
  187. return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
  188. }
  189. function translate(transform, p0, p1) {
  190. var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
  191. return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
  192. }
  193. function centroid(extent) {
  194. return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
  195. }
  196. function schedule(transition, transform, point) {
  197. transition
  198. .on("start.zoom", function() { gesture(this, arguments).start(); })
  199. .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); })
  200. .tween("zoom", function() {
  201. var that = this,
  202. args = arguments,
  203. g = gesture(that, args),
  204. e = extent.apply(that, args),
  205. p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point,
  206. w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
  207. a = that.__zoom,
  208. b = typeof transform === "function" ? transform.apply(that, args) : transform,
  209. i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
  210. return function(t) {
  211. if (t === 1) t = b; // Avoid rounding error on end.
  212. else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
  213. g.zoom(null, t);
  214. };
  215. });
  216. }
  217. function gesture(that, args, clean) {
  218. return (!clean && that.__zooming) || new Gesture(that, args);
  219. }
  220. function Gesture(that, args) {
  221. this.that = that;
  222. this.args = args;
  223. this.active = 0;
  224. this.extent = extent.apply(that, args);
  225. this.taps = 0;
  226. }
  227. Gesture.prototype = {
  228. start: function() {
  229. if (++this.active === 1) {
  230. this.that.__zooming = this;
  231. this.emit("start");
  232. }
  233. return this;
  234. },
  235. zoom: function(key, transform) {
  236. if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
  237. if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
  238. if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
  239. this.that.__zoom = transform;
  240. this.emit("zoom");
  241. return this;
  242. },
  243. end: function() {
  244. if (--this.active === 0) {
  245. delete this.that.__zooming;
  246. this.emit("end");
  247. }
  248. return this;
  249. },
  250. emit: function(type) {
  251. d3Selection.customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);
  252. }
  253. };
  254. function wheeled() {
  255. if (!filter.apply(this, arguments)) return;
  256. var g = gesture(this, arguments),
  257. t = this.__zoom,
  258. k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
  259. p = d3Selection.mouse(this);
  260. // If the mouse is in the same location as before, reuse it.
  261. // If there were recent wheel events, reset the wheel idle timeout.
  262. if (g.wheel) {
  263. if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
  264. g.mouse[1] = t.invert(g.mouse[0] = p);
  265. }
  266. clearTimeout(g.wheel);
  267. }
  268. // If this wheel event won’t trigger a transform change, ignore it.
  269. else if (t.k === k) return;
  270. // Otherwise, capture the mouse point and location at the start.
  271. else {
  272. g.mouse = [p, t.invert(p)];
  273. d3Transition.interrupt(this);
  274. g.start();
  275. }
  276. noevent();
  277. g.wheel = setTimeout(wheelidled, wheelDelay);
  278. g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
  279. function wheelidled() {
  280. g.wheel = null;
  281. g.end();
  282. }
  283. }
  284. function mousedowned() {
  285. if (touchending || !filter.apply(this, arguments)) return;
  286. var g = gesture(this, arguments, true),
  287. v = d3Selection.select(d3Selection.event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
  288. p = d3Selection.mouse(this),
  289. x0 = d3Selection.event.clientX,
  290. y0 = d3Selection.event.clientY;
  291. d3Drag.dragDisable(d3Selection.event.view);
  292. nopropagation();
  293. g.mouse = [p, this.__zoom.invert(p)];
  294. d3Transition.interrupt(this);
  295. g.start();
  296. function mousemoved() {
  297. noevent();
  298. if (!g.moved) {
  299. var dx = d3Selection.event.clientX - x0, dy = d3Selection.event.clientY - y0;
  300. g.moved = dx * dx + dy * dy > clickDistance2;
  301. }
  302. g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = d3Selection.mouse(g.that), g.mouse[1]), g.extent, translateExtent));
  303. }
  304. function mouseupped() {
  305. v.on("mousemove.zoom mouseup.zoom", null);
  306. d3Drag.dragEnable(d3Selection.event.view, g.moved);
  307. noevent();
  308. g.end();
  309. }
  310. }
  311. function dblclicked() {
  312. if (!filter.apply(this, arguments)) return;
  313. var t0 = this.__zoom,
  314. p0 = d3Selection.mouse(this),
  315. p1 = t0.invert(p0),
  316. k1 = t0.k * (d3Selection.event.shiftKey ? 0.5 : 2),
  317. t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent);
  318. noevent();
  319. if (duration > 0) d3Selection.select(this).transition().duration(duration).call(schedule, t1, p0);
  320. else d3Selection.select(this).call(zoom.transform, t1);
  321. }
  322. function touchstarted() {
  323. if (!filter.apply(this, arguments)) return;
  324. var touches = d3Selection.event.touches,
  325. n = touches.length,
  326. g = gesture(this, arguments, d3Selection.event.changedTouches.length === n),
  327. started, i, t, p;
  328. nopropagation();
  329. for (i = 0; i < n; ++i) {
  330. t = touches[i], p = d3Selection.touch(this, touches, t.identifier);
  331. p = [p, this.__zoom.invert(p), t.identifier];
  332. if (!g.touch0) g.touch0 = p, started = true, g.taps = 1 + !!touchstarting;
  333. else if (!g.touch1 && g.touch0[2] !== p[2]) g.touch1 = p, g.taps = 0;
  334. }
  335. if (touchstarting) touchstarting = clearTimeout(touchstarting);
  336. if (started) {
  337. if (g.taps < 2) touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay);
  338. d3Transition.interrupt(this);
  339. g.start();
  340. }
  341. }
  342. function touchmoved() {
  343. if (!this.__zooming) return;
  344. var g = gesture(this, arguments),
  345. touches = d3Selection.event.changedTouches,
  346. n = touches.length, i, t, p, l;
  347. noevent();
  348. if (touchstarting) touchstarting = clearTimeout(touchstarting);
  349. g.taps = 0;
  350. for (i = 0; i < n; ++i) {
  351. t = touches[i], p = d3Selection.touch(this, touches, t.identifier);
  352. if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;
  353. else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;
  354. }
  355. t = g.that.__zoom;
  356. if (g.touch1) {
  357. var p0 = g.touch0[0], l0 = g.touch0[1],
  358. p1 = g.touch1[0], l1 = g.touch1[1],
  359. dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
  360. dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
  361. t = scale(t, Math.sqrt(dp / dl));
  362. p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
  363. l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
  364. }
  365. else if (g.touch0) p = g.touch0[0], l = g.touch0[1];
  366. else return;
  367. g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
  368. }
  369. function touchended() {
  370. if (!this.__zooming) return;
  371. var g = gesture(this, arguments),
  372. touches = d3Selection.event.changedTouches,
  373. n = touches.length, i, t;
  374. nopropagation();
  375. if (touchending) clearTimeout(touchending);
  376. touchending = setTimeout(function() { touchending = null; }, touchDelay);
  377. for (i = 0; i < n; ++i) {
  378. t = touches[i];
  379. if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;
  380. else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
  381. }
  382. if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
  383. if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);
  384. else {
  385. g.end();
  386. // If this was a dbltap, reroute to the (optional) dblclick.zoom handler.
  387. if (g.taps === 2) {
  388. var p = d3Selection.select(this).on("dblclick.zoom");
  389. if (p) p.apply(this, arguments);
  390. }
  391. }
  392. }
  393. zoom.wheelDelta = function(_) {
  394. return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant(+_), zoom) : wheelDelta;
  395. };
  396. zoom.filter = function(_) {
  397. return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), zoom) : filter;
  398. };
  399. zoom.touchable = function(_) {
  400. return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), zoom) : touchable;
  401. };
  402. zoom.extent = function(_) {
  403. return arguments.length ? (extent = typeof _ === "function" ? _ : constant([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
  404. };
  405. zoom.scaleExtent = function(_) {
  406. return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
  407. };
  408. zoom.translateExtent = function(_) {
  409. return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]];
  410. };
  411. zoom.constrain = function(_) {
  412. return arguments.length ? (constrain = _, zoom) : constrain;
  413. };
  414. zoom.duration = function(_) {
  415. return arguments.length ? (duration = +_, zoom) : duration;
  416. };
  417. zoom.interpolate = function(_) {
  418. return arguments.length ? (interpolate = _, zoom) : interpolate;
  419. };
  420. zoom.on = function() {
  421. var value = listeners.on.apply(listeners, arguments);
  422. return value === listeners ? zoom : value;
  423. };
  424. zoom.clickDistance = function(_) {
  425. return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
  426. };
  427. return zoom;
  428. }
  429. exports.zoom = zoom;
  430. exports.zoomIdentity = identity;
  431. exports.zoomTransform = transform;
  432. Object.defineProperty(exports, '__esModule', { value: true });
  433. }));