d3-brush.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. // https://d3js.org/d3-brush/ v1.1.5 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 BrushEvent(target, type, selection) {
  13. this.target = target;
  14. this.type = type;
  15. this.selection = selection;
  16. }
  17. function nopropagation() {
  18. d3Selection.event.stopImmediatePropagation();
  19. }
  20. function noevent() {
  21. d3Selection.event.preventDefault();
  22. d3Selection.event.stopImmediatePropagation();
  23. }
  24. var MODE_DRAG = {name: "drag"},
  25. MODE_SPACE = {name: "space"},
  26. MODE_HANDLE = {name: "handle"},
  27. MODE_CENTER = {name: "center"};
  28. function number1(e) {
  29. return [+e[0], +e[1]];
  30. }
  31. function number2(e) {
  32. return [number1(e[0]), number1(e[1])];
  33. }
  34. function toucher(identifier) {
  35. return function(target) {
  36. return d3Selection.touch(target, d3Selection.event.touches, identifier);
  37. };
  38. }
  39. var X = {
  40. name: "x",
  41. handles: ["w", "e"].map(type),
  42. input: function(x, e) { return x == null ? null : [[+x[0], e[0][1]], [+x[1], e[1][1]]]; },
  43. output: function(xy) { return xy && [xy[0][0], xy[1][0]]; }
  44. };
  45. var Y = {
  46. name: "y",
  47. handles: ["n", "s"].map(type),
  48. input: function(y, e) { return y == null ? null : [[e[0][0], +y[0]], [e[1][0], +y[1]]]; },
  49. output: function(xy) { return xy && [xy[0][1], xy[1][1]]; }
  50. };
  51. var XY = {
  52. name: "xy",
  53. handles: ["n", "w", "e", "s", "nw", "ne", "sw", "se"].map(type),
  54. input: function(xy) { return xy == null ? null : number2(xy); },
  55. output: function(xy) { return xy; }
  56. };
  57. var cursors = {
  58. overlay: "crosshair",
  59. selection: "move",
  60. n: "ns-resize",
  61. e: "ew-resize",
  62. s: "ns-resize",
  63. w: "ew-resize",
  64. nw: "nwse-resize",
  65. ne: "nesw-resize",
  66. se: "nwse-resize",
  67. sw: "nesw-resize"
  68. };
  69. var flipX = {
  70. e: "w",
  71. w: "e",
  72. nw: "ne",
  73. ne: "nw",
  74. se: "sw",
  75. sw: "se"
  76. };
  77. var flipY = {
  78. n: "s",
  79. s: "n",
  80. nw: "sw",
  81. ne: "se",
  82. se: "ne",
  83. sw: "nw"
  84. };
  85. var signsX = {
  86. overlay: +1,
  87. selection: +1,
  88. n: null,
  89. e: +1,
  90. s: null,
  91. w: -1,
  92. nw: -1,
  93. ne: +1,
  94. se: +1,
  95. sw: -1
  96. };
  97. var signsY = {
  98. overlay: +1,
  99. selection: +1,
  100. n: -1,
  101. e: null,
  102. s: +1,
  103. w: null,
  104. nw: -1,
  105. ne: -1,
  106. se: +1,
  107. sw: +1
  108. };
  109. function type(t) {
  110. return {type: t};
  111. }
  112. // Ignore right-click, since that should open the context menu.
  113. function defaultFilter() {
  114. return !d3Selection.event.ctrlKey && !d3Selection.event.button;
  115. }
  116. function defaultExtent() {
  117. var svg = this.ownerSVGElement || this;
  118. if (svg.hasAttribute("viewBox")) {
  119. svg = svg.viewBox.baseVal;
  120. return [[svg.x, svg.y], [svg.x + svg.width, svg.y + svg.height]];
  121. }
  122. return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]];
  123. }
  124. function defaultTouchable() {
  125. return navigator.maxTouchPoints || ("ontouchstart" in this);
  126. }
  127. // Like d3.local, but with the name “__brush” rather than auto-generated.
  128. function local(node) {
  129. while (!node.__brush) if (!(node = node.parentNode)) return;
  130. return node.__brush;
  131. }
  132. function empty(extent) {
  133. return extent[0][0] === extent[1][0]
  134. || extent[0][1] === extent[1][1];
  135. }
  136. function brushSelection(node) {
  137. var state = node.__brush;
  138. return state ? state.dim.output(state.selection) : null;
  139. }
  140. function brushX() {
  141. return brush$1(X);
  142. }
  143. function brushY() {
  144. return brush$1(Y);
  145. }
  146. function brush() {
  147. return brush$1(XY);
  148. }
  149. function brush$1(dim) {
  150. var extent = defaultExtent,
  151. filter = defaultFilter,
  152. touchable = defaultTouchable,
  153. keys = true,
  154. listeners = d3Dispatch.dispatch("start", "brush", "end"),
  155. handleSize = 6,
  156. touchending;
  157. function brush(group) {
  158. var overlay = group
  159. .property("__brush", initialize)
  160. .selectAll(".overlay")
  161. .data([type("overlay")]);
  162. overlay.enter().append("rect")
  163. .attr("class", "overlay")
  164. .attr("pointer-events", "all")
  165. .attr("cursor", cursors.overlay)
  166. .merge(overlay)
  167. .each(function() {
  168. var extent = local(this).extent;
  169. d3Selection.select(this)
  170. .attr("x", extent[0][0])
  171. .attr("y", extent[0][1])
  172. .attr("width", extent[1][0] - extent[0][0])
  173. .attr("height", extent[1][1] - extent[0][1]);
  174. });
  175. group.selectAll(".selection")
  176. .data([type("selection")])
  177. .enter().append("rect")
  178. .attr("class", "selection")
  179. .attr("cursor", cursors.selection)
  180. .attr("fill", "#777")
  181. .attr("fill-opacity", 0.3)
  182. .attr("stroke", "#fff")
  183. .attr("shape-rendering", "crispEdges");
  184. var handle = group.selectAll(".handle")
  185. .data(dim.handles, function(d) { return d.type; });
  186. handle.exit().remove();
  187. handle.enter().append("rect")
  188. .attr("class", function(d) { return "handle handle--" + d.type; })
  189. .attr("cursor", function(d) { return cursors[d.type]; });
  190. group
  191. .each(redraw)
  192. .attr("fill", "none")
  193. .attr("pointer-events", "all")
  194. .on("mousedown.brush", started)
  195. .filter(touchable)
  196. .on("touchstart.brush", started)
  197. .on("touchmove.brush", touchmoved)
  198. .on("touchend.brush touchcancel.brush", touchended)
  199. .style("touch-action", "none")
  200. .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
  201. }
  202. brush.move = function(group, selection) {
  203. if (group.selection) {
  204. group
  205. .on("start.brush", function() { emitter(this, arguments).beforestart().start(); })
  206. .on("interrupt.brush end.brush", function() { emitter(this, arguments).end(); })
  207. .tween("brush", function() {
  208. var that = this,
  209. state = that.__brush,
  210. emit = emitter(that, arguments),
  211. selection0 = state.selection,
  212. selection1 = dim.input(typeof selection === "function" ? selection.apply(this, arguments) : selection, state.extent),
  213. i = d3Interpolate.interpolate(selection0, selection1);
  214. function tween(t) {
  215. state.selection = t === 1 && selection1 === null ? null : i(t);
  216. redraw.call(that);
  217. emit.brush();
  218. }
  219. return selection0 !== null && selection1 !== null ? tween : tween(1);
  220. });
  221. } else {
  222. group
  223. .each(function() {
  224. var that = this,
  225. args = arguments,
  226. state = that.__brush,
  227. selection1 = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent),
  228. emit = emitter(that, args).beforestart();
  229. d3Transition.interrupt(that);
  230. state.selection = selection1 === null ? null : selection1;
  231. redraw.call(that);
  232. emit.start().brush().end();
  233. });
  234. }
  235. };
  236. brush.clear = function(group) {
  237. brush.move(group, null);
  238. };
  239. function redraw() {
  240. var group = d3Selection.select(this),
  241. selection = local(this).selection;
  242. if (selection) {
  243. group.selectAll(".selection")
  244. .style("display", null)
  245. .attr("x", selection[0][0])
  246. .attr("y", selection[0][1])
  247. .attr("width", selection[1][0] - selection[0][0])
  248. .attr("height", selection[1][1] - selection[0][1]);
  249. group.selectAll(".handle")
  250. .style("display", null)
  251. .attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; })
  252. .attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; })
  253. .attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + handleSize : handleSize; })
  254. .attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + handleSize : handleSize; });
  255. }
  256. else {
  257. group.selectAll(".selection,.handle")
  258. .style("display", "none")
  259. .attr("x", null)
  260. .attr("y", null)
  261. .attr("width", null)
  262. .attr("height", null);
  263. }
  264. }
  265. function emitter(that, args, clean) {
  266. return (!clean && that.__brush.emitter) || new Emitter(that, args);
  267. }
  268. function Emitter(that, args) {
  269. this.that = that;
  270. this.args = args;
  271. this.state = that.__brush;
  272. this.active = 0;
  273. }
  274. Emitter.prototype = {
  275. beforestart: function() {
  276. if (++this.active === 1) this.state.emitter = this, this.starting = true;
  277. return this;
  278. },
  279. start: function() {
  280. if (this.starting) this.starting = false, this.emit("start");
  281. else this.emit("brush");
  282. return this;
  283. },
  284. brush: function() {
  285. this.emit("brush");
  286. return this;
  287. },
  288. end: function() {
  289. if (--this.active === 0) delete this.state.emitter, this.emit("end");
  290. return this;
  291. },
  292. emit: function(type) {
  293. d3Selection.customEvent(new BrushEvent(brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]);
  294. }
  295. };
  296. function started() {
  297. if (touchending && !d3Selection.event.touches) return;
  298. if (!filter.apply(this, arguments)) return;
  299. var that = this,
  300. type = d3Selection.event.target.__data__.type,
  301. mode = (keys && d3Selection.event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (keys && d3Selection.event.altKey ? MODE_CENTER : MODE_HANDLE),
  302. signX = dim === Y ? null : signsX[type],
  303. signY = dim === X ? null : signsY[type],
  304. state = local(that),
  305. extent = state.extent,
  306. selection = state.selection,
  307. W = extent[0][0], w0, w1,
  308. N = extent[0][1], n0, n1,
  309. E = extent[1][0], e0, e1,
  310. S = extent[1][1], s0, s1,
  311. dx = 0,
  312. dy = 0,
  313. moving,
  314. shifting = signX && signY && keys && d3Selection.event.shiftKey,
  315. lockX,
  316. lockY,
  317. pointer = d3Selection.event.touches ? toucher(d3Selection.event.changedTouches[0].identifier) : d3Selection.mouse,
  318. point0 = pointer(that),
  319. point = point0,
  320. emit = emitter(that, arguments, true).beforestart();
  321. if (type === "overlay") {
  322. if (selection) moving = true;
  323. state.selection = selection = [
  324. [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]],
  325. [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0]
  326. ];
  327. } else {
  328. w0 = selection[0][0];
  329. n0 = selection[0][1];
  330. e0 = selection[1][0];
  331. s0 = selection[1][1];
  332. }
  333. w1 = w0;
  334. n1 = n0;
  335. e1 = e0;
  336. s1 = s0;
  337. var group = d3Selection.select(that)
  338. .attr("pointer-events", "none");
  339. var overlay = group.selectAll(".overlay")
  340. .attr("cursor", cursors[type]);
  341. if (d3Selection.event.touches) {
  342. emit.moved = moved;
  343. emit.ended = ended;
  344. } else {
  345. var view = d3Selection.select(d3Selection.event.view)
  346. .on("mousemove.brush", moved, true)
  347. .on("mouseup.brush", ended, true);
  348. if (keys) view
  349. .on("keydown.brush", keydowned, true)
  350. .on("keyup.brush", keyupped, true);
  351. d3Drag.dragDisable(d3Selection.event.view);
  352. }
  353. nopropagation();
  354. d3Transition.interrupt(that);
  355. redraw.call(that);
  356. emit.start();
  357. function moved() {
  358. var point1 = pointer(that);
  359. if (shifting && !lockX && !lockY) {
  360. if (Math.abs(point1[0] - point[0]) > Math.abs(point1[1] - point[1])) lockY = true;
  361. else lockX = true;
  362. }
  363. point = point1;
  364. moving = true;
  365. noevent();
  366. move();
  367. }
  368. function move() {
  369. var t;
  370. dx = point[0] - point0[0];
  371. dy = point[1] - point0[1];
  372. switch (mode) {
  373. case MODE_SPACE:
  374. case MODE_DRAG: {
  375. if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx;
  376. if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy;
  377. break;
  378. }
  379. case MODE_HANDLE: {
  380. if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0;
  381. else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx;
  382. if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0;
  383. else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy;
  384. break;
  385. }
  386. case MODE_CENTER: {
  387. if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX));
  388. if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY));
  389. break;
  390. }
  391. }
  392. if (e1 < w1) {
  393. signX *= -1;
  394. t = w0, w0 = e0, e0 = t;
  395. t = w1, w1 = e1, e1 = t;
  396. if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]);
  397. }
  398. if (s1 < n1) {
  399. signY *= -1;
  400. t = n0, n0 = s0, s0 = t;
  401. t = n1, n1 = s1, s1 = t;
  402. if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]);
  403. }
  404. if (state.selection) selection = state.selection; // May be set by brush.move!
  405. if (lockX) w1 = selection[0][0], e1 = selection[1][0];
  406. if (lockY) n1 = selection[0][1], s1 = selection[1][1];
  407. if (selection[0][0] !== w1
  408. || selection[0][1] !== n1
  409. || selection[1][0] !== e1
  410. || selection[1][1] !== s1) {
  411. state.selection = [[w1, n1], [e1, s1]];
  412. redraw.call(that);
  413. emit.brush();
  414. }
  415. }
  416. function ended() {
  417. nopropagation();
  418. if (d3Selection.event.touches) {
  419. if (d3Selection.event.touches.length) return;
  420. if (touchending) clearTimeout(touchending);
  421. touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
  422. } else {
  423. d3Drag.dragEnable(d3Selection.event.view, moving);
  424. view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null);
  425. }
  426. group.attr("pointer-events", "all");
  427. overlay.attr("cursor", cursors.overlay);
  428. if (state.selection) selection = state.selection; // May be set by brush.move (on start)!
  429. if (empty(selection)) state.selection = null, redraw.call(that);
  430. emit.end();
  431. }
  432. function keydowned() {
  433. switch (d3Selection.event.keyCode) {
  434. case 16: { // SHIFT
  435. shifting = signX && signY;
  436. break;
  437. }
  438. case 18: { // ALT
  439. if (mode === MODE_HANDLE) {
  440. if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
  441. if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
  442. mode = MODE_CENTER;
  443. move();
  444. }
  445. break;
  446. }
  447. case 32: { // SPACE; takes priority over ALT
  448. if (mode === MODE_HANDLE || mode === MODE_CENTER) {
  449. if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx;
  450. if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy;
  451. mode = MODE_SPACE;
  452. overlay.attr("cursor", cursors.selection);
  453. move();
  454. }
  455. break;
  456. }
  457. default: return;
  458. }
  459. noevent();
  460. }
  461. function keyupped() {
  462. switch (d3Selection.event.keyCode) {
  463. case 16: { // SHIFT
  464. if (shifting) {
  465. lockX = lockY = shifting = false;
  466. move();
  467. }
  468. break;
  469. }
  470. case 18: { // ALT
  471. if (mode === MODE_CENTER) {
  472. if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
  473. if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
  474. mode = MODE_HANDLE;
  475. move();
  476. }
  477. break;
  478. }
  479. case 32: { // SPACE
  480. if (mode === MODE_SPACE) {
  481. if (d3Selection.event.altKey) {
  482. if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
  483. if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
  484. mode = MODE_CENTER;
  485. } else {
  486. if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
  487. if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
  488. mode = MODE_HANDLE;
  489. }
  490. overlay.attr("cursor", cursors[type]);
  491. move();
  492. }
  493. break;
  494. }
  495. default: return;
  496. }
  497. noevent();
  498. }
  499. }
  500. function touchmoved() {
  501. emitter(this, arguments).moved();
  502. }
  503. function touchended() {
  504. emitter(this, arguments).ended();
  505. }
  506. function initialize() {
  507. var state = this.__brush || {selection: null};
  508. state.extent = number2(extent.apply(this, arguments));
  509. state.dim = dim;
  510. return state;
  511. }
  512. brush.extent = function(_) {
  513. return arguments.length ? (extent = typeof _ === "function" ? _ : constant(number2(_)), brush) : extent;
  514. };
  515. brush.filter = function(_) {
  516. return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), brush) : filter;
  517. };
  518. brush.touchable = function(_) {
  519. return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), brush) : touchable;
  520. };
  521. brush.handleSize = function(_) {
  522. return arguments.length ? (handleSize = +_, brush) : handleSize;
  523. };
  524. brush.keyModifiers = function(_) {
  525. return arguments.length ? (keys = !!_, brush) : keys;
  526. };
  527. brush.on = function() {
  528. var value = listeners.on.apply(listeners, arguments);
  529. return value === listeners ? brush : value;
  530. };
  531. return brush;
  532. }
  533. exports.brush = brush;
  534. exports.brushSelection = brushSelection;
  535. exports.brushX = brushX;
  536. exports.brushY = brushY;
  537. Object.defineProperty(exports, '__esModule', { value: true });
  538. }));