11<template >
2- <div
3- :id =" this.axisElemID"
4- class =" vdp-axis"
5- :style =" {
6- 'height': this.computedHeight + 'px',
7- 'width': this.computedWidth + 'px',
8- 'top': this.computedTop + 'px',
9- 'left': this.computedLeft + 'px'
10- }" ></div >
2+ <div >
3+ <canvas
4+ :id =" this.axisElemID"
5+ class =" vdp-axis"
6+ :style =" {
7+ 'height': this.computedHeight + 'px',
8+ 'width': this.computedWidth + 'px',
9+ 'top': this.computedTop + 'px',
10+ 'left': this.computedLeft + 'px'
11+ }"
12+ ></canvas >
13+ <div v-show =" this.highlightX !== null && this.highlightY != null"
14+ :style =" {
15+ 'position': 'absolute',
16+ 'pointer-events': 'none',
17+ 'height': '14px',
18+ 'width': '14px',
19+ 'border-radius': '50%',
20+ 'opacity': 0.6,
21+ 'background-color': '#555',
22+ 'top': (this.computedTop + this.highlightY - 7) + 'px',
23+ 'left': (this.computedLeft + this.highlightX - 7) + 'px'
24+ }"
25+ class =" vdp-axis-highlight"
26+ ></div >
27+ </div >
1128</template >
1229
1330<script >
31+ import Two from ' two.js' ;
1432import { select as d3_select } from ' d3-selection' ;
1533import { cluster as d3_cluster , hierarchy as d3_hierarchy } from ' d3-hierarchy' ;
16-
17- import { svgAsPngUri } from ' save-svg-as-png' ;
34+ import { mouse as d3_mouse } from ' d3' ;
35+ import debounce from ' lodash/debounce' ;
36+ import { TOOLTIP_DEBOUNCE } from ' ./../../constants.js' ;
1837
1938import AbstractScale from ' ./../../scales/AbstractScale.js' ;
2039import DataContainer from ' ./../../data/DataContainer.js' ;
2140import HistoryEvent from ' ./../../history/HistoryEvent.js' ;
2241import HistoryStack , { computedParam } from ' ./../../history/HistoryStack.js' ;
23- import { filterHierarchy } from ' ../../helpers.js' ;
42+ import { filterHierarchy , getDelaunay } from ' ../../helpers.js' ;
43+
2444
2545import { EVENT_TYPES , EVENT_SUBTYPES } from ' ./../../history/base-events.js' ;
2646
@@ -100,7 +120,8 @@ export default {
100120 },
101121 data () {
102122 return {
103-
123+ highlightX: null ,
124+ highlightY: null ,
104125 }
105126 },
106127 computed: {
@@ -202,7 +223,15 @@ export default {
202223 removeAxis () {
203224 d3_select (this .axisSelector ).select (" svg" ).remove ();
204225 },
205- drawAxis () {
226+ tooltip (x , y ) {
227+ this .highlightX = x;
228+ this .highlightY = y;
229+ },
230+ tooltipDestroy () {
231+ this .highlightX = null ;
232+ this .highlightY = null ;
233+ },
234+ drawAxis (d3Node ) {
206235 const vm = this ;
207236 vm .removeAxis ();
208237
@@ -239,85 +268,115 @@ export default {
239268 const root = d3_hierarchy (hierarchyData);
240269 tree (root);
241270
271+ const descendants = root .descendants ();
272+
242273
243274 /*
244275 Draw the dendrogram
245276 */
246- const container = d3_select (vm .axisSelector )
247- .append (" svg" )
248- .attr (" width" , vm .computedWidth )
249- .attr (" height" , vm .computedHeight );
250-
251- const gTree = container .append (" g" )
252- .attr (" transform" , " translate(" + vm .computedTranslateX + " ," + vm .computedTranslateY + " )" );
277+ let canvas;
278+ if (d3Node) {
279+ canvas = d3Node;
280+ } else {
281+ canvas = d3_select (this .axisSelector );
282+ }
253283
284+ const canvasNode = canvas .node ();
285+
286+ const two = new Two ({
287+ width: vm .computedWidth ,
288+ height: vm .computedHeight ,
289+ domElement: canvasNode
290+ });
254291
255292 let pathFunction;
256293 if (this ._side === SIDES .TOP ) {
257294 pathFunction = (d ) => {
258- return " M" + d .parent .x + " ," + d .parent .y
259- + " H" + d .x
260- + " M" + d .x + " ," + d .y
261- + " V" + d .parent .y ;
295+ return two .makePath (
296+ d .parent .x + vm .computedTranslateX , d .parent .y + vm .computedTranslateY , // M
297+ d .x + vm .computedTranslateX , d .parent .y + vm .computedTranslateY , // H d.x
298+ d .x + vm .computedTranslateX , d .y + vm .computedTranslateY , // M
299+ d .x + vm .computedTranslateX , d .parent .y + vm .computedTranslateY // V d.parent.y
300+ );
262301 }
263302 } else if (this ._side === SIDES .BOTTOM ) {
264303 pathFunction = (d ) => {
265- return " M" + d .parent .x + " ," + (vm .pMarginBottom - d .parent .y )
266- + " H" + d .x
267- + " M" + d .x + " ," + (vm .pMarginBottom - d .y )
268- + " V" + (vm .pMarginBottom - d .parent .y );
304+ return two .makePath (
305+ d .parent .x + vm .computedTranslateX , (vm .pMarginBottom - d .parent .y ) + vm .computedTranslateY , // M
306+ d .x + vm .computedTranslateX , (vm .pMarginBottom - d .parent .y ) + vm .computedTranslateY , // H d.x
307+ d .x + vm .computedTranslateX , (vm .pMarginBottom - d .y ) + vm .computedTranslateY , // M
308+ d .x + vm .computedTranslateX , (vm .pMarginBottom - d .parent .y ) + vm .computedTranslateY // V (vm.pMarginBottom - d.parent.y)
309+ );
269310 }
270311 }
271312
272- gTree .selectAll (" .link" )
273- .data (root .descendants ().slice (1 ))
274- .enter ().append (" path" )
275- .attr (" class" , " link" )
276- .attr (" d" , pathFunction)
277- .attr (" fill" , " none" )
278- .attr (" stroke" , " #555" )
279- .attr (" stroke-opacity" , 0.6 )
280- .attr (" stroke-width" , " 1.5px" );
281-
282313 let nodeTransformFunction;
283314 if (this ._side === SIDES .TOP ) {
284315 nodeTransformFunction = (d ) => {
285- return " translate( " + d .x + " , " + d .y + " ) " ;
316+ return [ d .x + vm . computedTranslateX , d .y + vm . computedTranslateY ] ;
286317 }
287318 } else if (this ._side === SIDES .BOTTOM ) {
288319 nodeTransformFunction = (d ) => {
289- return " translate( " + d .x + " , " + (vm .pMarginBottom - d .y ) + " ) " ;
320+ return [ d .x + vm . computedTranslateX , (vm .pMarginBottom - d .y ) + vm . computedTranslateY ];
290321 }
291322 }
292323
293- const nodes = gTree .selectAll (" .node" )
294- .data (root .descendants ())
295- .enter ().append (" g" )
296- .attr (" class" , function (d ) { return " node" + (d .children ? " node--internal" : " node--leaf" ); })
297- .attr (" transform" , nodeTransformFunction);
298-
299- /* nodes.append("text")
300- .style("display", (d) => { return d.children ? 'none' : 'normal'; })
301- .text((d) => { return d.data.name; })
302- .style("font", "10px sans-serif")
303- .style("text-anchor", "end")
304- .attr("dx", "-.6em")
305- .attr("dy", ".6em")
306- .attr("transform", "rotate(-65)"); */
307-
308- // filtering buttons
309- nodes .append (" circle" )
310- .attr (" r" , 7 )
311- .attr (" fill" , " #555" )
312- .attr (" fill-opacity" , 0 )
313- .style (" cursor" , " pointer" )
314- .on (" mouseover" , function () {
315- d3_select (this ).attr (" fill-opacity" , 0.6 );
316- })
317- .on (" mouseleave" , function () {
318- d3_select (this ).attr (" fill-opacity" , 0 );
319- })
320- .on (" click" , (d ) => {
324+ const nodePoints = [];
325+
326+ descendants .forEach ((d , i ) => {
327+ if (i > 0 ) {
328+ const path = pathFunction (d);
329+ path .noFill ();
330+ path .stroke = " #555" ;
331+ path .opacity = 0.6 ;
332+ path .linewidth = 1.5 ;
333+ }
334+ nodePoints .push (nodeTransformFunction (d));
335+ });
336+
337+
338+ two .update ();
339+
340+ if (d3Node) {
341+ return ;
342+ }
343+
344+ const delaunay = getDelaunay (nodePoints, true );
345+
346+ /*
347+ * Listen for mouse events
348+ */
349+ const getDataFromMouse = (mouseX , mouseY ) => {
350+ const i = delaunay .find (mouseX, mouseY);
351+ return descendants[i];
352+ };
353+
354+ const debouncedTooltipDestroy = debounce (vm .tooltipDestroy , TOOLTIP_DEBOUNCE );
355+ canvas .on (" mousemove" , () => {
356+ const mouse = d3_mouse (canvasNode);
357+ const mouseX = mouse[0 ];
358+ const mouseY = mouse[1 ];
359+
360+ const node = getDataFromMouse (mouseX, mouseY);
361+
362+ if (node) {
363+ const nodePoint = nodeTransformFunction (node);
364+ vm .tooltip (nodePoint[0 ], nodePoint[1 ]);
365+ } else {
366+ debouncedTooltipDestroy (node);
367+ }
368+ })
369+ .on (" mouseleave" , vm .tooltipDestroy );
370+
371+ canvas .on (" click" , () => {
372+ const mouse = d3_mouse (canvasNode);
373+ const mouseX = mouse[0 ];
374+ const mouseY = mouse[1 ];
375+
376+ const node = getDataFromMouse (mouseX, mouseY);
377+
378+ if (node) {
379+ /*
321380 varScale.sortByHierarchy(vm._hierarchyContainer);
322381 stack.push(new HistoryEvent(
323382 EVENT_TYPES.SCALE,
@@ -326,24 +385,18 @@ export default {
326385 "sortByHierarchy",
327386 [computedParam(EVENT_TYPES.DATA, [vm.h])]
328387 ));
329- varScale .filterByHierarchy (vm ._hierarchyContainer , d .data .name );
388+ */
389+ varScale .filterByHierarchy (vm ._hierarchyContainer , node .data .name );
330390 stack .push (new HistoryEvent (
331391 EVENT_TYPES .SCALE ,
332392 EVENT_SUBTYPES .SCALE_DOMAIN_FILTER ,
333393 vm .variable ,
334394 " filterByHierarchy" ,
335- [computedParam (EVENT_TYPES .DATA , [vm .h ]), d .data .name ]
395+ [computedParam (EVENT_TYPES .DATA , [vm .h ]), node .data .name ]
336396 ));
337- });
338-
339- },
340- downloadAxis () {
341- const node = d3_select (this .axisSelector ).select (" svg" ).node ();
342- return new Promise ((resolve , reject ) => {
343- svgAsPngUri (node, {}, (uri ) => {
344- resolve (uri);
345- });
397+ }
346398 });
399+
347400 }
348401 }
349402}
0 commit comments