Update: update traceTree (#212)
authorAllen Wang <Allen.Wang.123@outlook.com>
Mon, 24 Dec 2018 16:28:17 +0000 (00:28 +0800)
committer吴晟 Wu Sheng <wu.sheng@foxmail.com>
Mon, 24 Dec 2018 16:28:17 +0000 (00:28 +0800)
1. Add tooltip plugin.
2. Add the connection between timeline span & tree span.
3. remove unnessory event bind and nodes.
4. update the tree display.

package.json
src/components/TraceTree/d3-trace.js
src/components/TraceTree/style.less

index d080282..ea3d263 100755 (executable)
@@ -43,6 +43,7 @@
     "cytoscape-cose-bilkent": "^4.0.0",
     "cytoscape-dagre": "^2.2.1",
     "d3": "^5.7.0",
+    "d3-tip": "^0.9.1",
     "dva": "^2.2.3",
     "dva-loading": "^2.0.3",
     "enquire-js": "^0.2.1",
index 3d8a709..1645581 100644 (file)
@@ -17,7 +17,7 @@
 
 /* eslint-disable */
 import * as d3 from 'd3';
-
+import d3tip from 'd3-tip';
 export default class TraceMap {
   constructor(el) {
     this.type = {
@@ -44,7 +44,7 @@ export default class TraceMap {
       .remove();
     this.width = this.el.clientWidth;
     this.height = this.el.clientHeight;
-    this.draw(this.data,this.row,this.showSpanModal);
+    this.draw(this.data, this.row);
   }
   draw(data, row, showSpanModal) {
     this.showSpanModal = showSpanModal;
@@ -72,7 +72,9 @@ export default class TraceMap {
       .append('svg')
       .attr('width', this.width)
       .attr('height', this.height);
-    this.timeGroup = this.body.append('g').attr('transform', d => 'translate(5,30)');
+    this.timeGroup = this.body
+      .append('g')
+      .attr('transform', d => 'translate(5,30)');
     const main = this.body
       .append('g')
       .attr('transform', d => 'translate(0,' + this.row.length * 9 + ')');
@@ -84,155 +86,188 @@ export default class TraceMap {
       .append('g')
       .attr('transform', `translate(5,20)`)
       .call(this.xAxis);
+
+    this.updatexAxis(this.root);
     this.update(this.root);
   }
-  update(source) {
+  updatexAxis(source) {
     const treeData = this.treemap(this.root);
     const nodes = treeData.descendants(),
       links = treeData.descendants().slice(1);
     let index = -1;
     nodes.forEach(function(d) {
       d.y = d.depth * 200;
-      d.timeX = ++index * 7;
-    });
-
-    this.body.call(this.getZoomBehavior(this.svg));
-
-    const node = this.svg.selectAll('g.node').data(nodes, d => {
-      return d.id || (d.id = ++this.i);
+      d.timeX = ++index * 12;
     });
+    // time
     const timeNode = this.timeGroup.selectAll('g.time').data(nodes, d => {
-      return d.id || (d.id = ++this.j);
+      return d.id;
     });
+    this.timeTip = d3tip()
+      .attr('class', 'd3-tip')
+      .offset([-10, 0])
+      .html(function(d) {
+        return d.data.label;
+      });
+    this.body.call(this.timeTip);
 
-    // time
+    const that = this;
     const timeEnter = timeNode
       .enter()
       .append('g')
       .attr('class', 'time')
-      .attr('transform', d => 'translate(' + 0 + ',' + d.timeX + ')');
+      .attr('transform', d => 'translate(' + 0 + ',' + d.timeX + ')')
+      .on('mouseover', function(d, i) {
+        that.timeTip.show(d, that.timeUpdate._groups[0][i].children[1]);
+        that.tip.show(d, that.nodeUpdate._groups[0][i]);
+      })
+      .on('mouseout', function(d, i) {
+        that.timeTip.hide(d, that.timeUpdate._groups[0][i].children[1]);
+        that.tip.hide(d, that.nodeUpdate._groups[0][i]);
+      })
+      .on('click', (d, i) => {
+        this.showSpanModal(
+          d.data,
+          { width: '100%', top: -10, left: '0' },
+          d3.select(that.timeUpdate._groups[0][i]).append('rect')
+        );
+        d3.event.stopPropagation();
+      });
+    this.timeEnter = timeEnter;
     timeEnter
       .append('rect')
-      .attr('height', 5)
+      .attr('height', 10)
+      .attr('width', this.width)
+      .attr('y', -4)
+      .attr('class', 'time-bg');
+    timeEnter
+      .append('rect')
+      .attr('class', 'time-inner')
+      .attr('height', 8)
       .attr('width', d => {
         if (!d.data.endTime || !d.data.startTime) return 0;
         return this.xScale(d.data.endTime - d.data.startTime) + 1;
       })
       .attr('rx', 2)
       .attr('ry', 2)
-      .attr(
-        'x',
-        d => (!d.data.endTime || !d.data.startTime ? 0 : this.xScale(d.data.startTime - this.min))
+      .attr('x', d =>
+        !d.data.endTime || !d.data.startTime
+          ? 0
+          : this.xScale(d.data.startTime - this.min)
       )
       .attr('y', -3)
-      .style('fill', d => `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}`);
-    var timeUpdate = timeEnter.merge(timeNode);
-
-    timeUpdate
+      .style(
+        'fill',
+        d =>
+          `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}`
+      );
+    this.timeUpdate = timeEnter.merge(timeNode);
+    this.timeUpdate
       .transition()
       .duration(600)
       .attr('transform', function(d) {
         return 'translate(' + 0 + ',' + d.timeX + ')';
       });
+
     const timeExit = timeNode
       .exit()
       .transition()
       .duration(600)
       .attr('transform', function(d) {
-        return 'translate(' + 0 + ',' + 8 + ')';
+        return 'translate(' + 0 + ',' + 10 + ')';
       })
       .remove();
+  }
+  update(source) {
+    const treeData = this.treemap(this.root);
+    const nodes = treeData.descendants(),
+      links = treeData.descendants().slice(1);
+    let index = -1;
+    nodes.forEach(function(d) {
+      d.y = d.depth * 200;
+      d.timeX = ++index * 10;
+    });
+    this.tip = d3tip()
+      .attr('class', 'd3-tip')
+      .offset([-10, 0])
+      .html(function(d) {
+        return d.data.label;
+      });
+
+    this.body.call(this.getZoomBehavior(this.svg));
+    this.body.call(this.tip);
+    const node = this.svg.selectAll('g.node').data(nodes, d => {
+      return d.id || (d.id = ++this.i);
+    });
     // node
+    const that = this;
     const nodeEnter = node
       .enter()
       .append('g')
       .attr('class', 'node')
       .attr('transform', function(d) {
         return 'translate(' + source.y0 + ',' + source.x0 + ')';
-      });
+      })
+      .on('mouseover', function(d, i) {
+        that.tip.show(d, this);
+        that.timeTip.show(d, that.timeUpdate._groups[0][i].children[1]);
+      })
+      .on('mouseout', function(d, i) {
+        that.tip.hide(d, this);
+        that.timeTip.hide(d, that.timeUpdate._groups[0][i].children[1]);
+      })
+    .on('click', (d, i) => {
+      this.showSpanModal(
+        d.data,
+        { width: '100%', top: -10, left: '0' },
+        d3.select(nodeEnter._groups[0][i]).append('rect')
+      );
+      d3.event.stopPropagation();
+    });
     nodeEnter
       .append('rect')
       .attr('class', 'block')
       .attr('x', '0')
-      .attr('y', '-16')
-      .attr('rx', 4)
-      .attr('ry', 4)
+      .attr('y', '-20')
       .attr('fill', d => (d.data.isError ? '#ff57221a' : '#f7f7f7'))
-      .attr('stroke', d => (d.data.isError ? '#ff5722aa' : '#e4e4e4'))
-      .on('click', (d, i) => {
-        this.showSpanModal(
-          d.data,
-          { width: '100%', top: -10, left: '0' },
-          d3.select(nodeEnter._groups[0][i]).append('rect')
-        );
-      })
-      .on('mouseenter', function(currNode, i) {
-        d3.select(nodeEnter._groups[0][i])
-          .select('g')
-          .attr('opacity', 1);
-      })
-      .on('mouseleave', function(currNode, i) {
-        d3.select(nodeEnter._groups[0][i])
-          .select('g')
-          .attr('opacity', 0);
-      });
-
-    const tooltip = nodeEnter
-      .append('g')
-      .attr('opacity', 0)
-      .attr('transform', function(d) {
-        return 'translate(0,-40)';
-      });
+      .attr('stroke', d => (d.data.isError ? '#ff5722aa' : '#e4e4e4'));
 
-    tooltip
+    nodeEnter
       .append('rect')
-      .attr('class', 'tooltip-box')
-      .attr('rx', 4)
-      .attr('ry', 4)
-      .attr('width', function(d) {
-        return d.data.label.length * 6 + 20;
-      });
-    tooltip
-      .append('text')
-      .attr('dy', 14)
-      .attr('fill', '#fafafa')
-      .attr('dx', 10)
-      .text(function(d) {
-        return d.data.label;
+      .attr('class', 'content')
+      .style('fill', '#fff')
+      .attr('stroke', d => {
+        console.log(d);
+        return d.data.isError ? '#ff5722aa' : '#e4e4e4';
       });
     nodeEnter
+      .append('rect')
+      .attr('class', 'service')
+      .attr('x', '-0.5')
+      .attr('y', '-20.5')
+      .style(
+        'fill',
+        d =>
+          `${this.sequentialScale(this.list.indexOf(d.data.serviceCode))}`
+      );
+    nodeEnter
       .append('text')
-      .attr('dy', -4)
-      .attr('x', 5)
+      .attr('dy', 13)
+      .attr('dx', 10)
+      .attr('stroke', '#333')
       .attr('text-anchor', function(d) {
         return 'start';
       })
       .text(function(d) {
-        return d.data.label.length > 23 ? d.data.label.slice(0, 23) : d.data.label;
-      })
-      .on('click', (d, i) => {
-        this.showSpanModal(
-          d.data,
-          { width: '100%', top: -10, left: '0' },
-          d3.select(nodeEnter._groups[0][i]).append('rect')
-        );
-        d3.event.stopPropagation();
-      })
-      .on('mouseenter', function(currNode, i) {
-        d3.select(nodeEnter._groups[0][i])
-          .select('g')
-          .attr('opacity', 1);
-      })
-      .on('mouseleave', function(currNode, i) {
-        d3.select(nodeEnter._groups[0][i])
-          .select('g')
-          .attr('opacity', 0);
+        return d.data.label.length > 19
+          ? d.data.label.slice(0, 19)
+          : d.data.label;
       });
 
     nodeEnter
       .append('text')
-      .attr('dy', 12)
-      .attr('x', 8)
+      .attr('dy', -7)
+      .attr('dx', 12)
       .attr('text-anchor', function(d) {
         return 'start';
       })
@@ -244,78 +279,45 @@ export default class TraceMap {
       })
       .text(function(d) {
         return d.data.layer;
-      })
-      .on('click', (d, i) => {
-        this.showSpanModal(
-          d.data,
-          { width: '100%', top: -10, left: '0' },
-          d3.select(nodeEnter._groups[0][i]).append('rect')
-        );
-        d3.event.stopPropagation();
-      })
-      .on('mouseenter', function(currNode, i) {
-        d3.select(nodeEnter._groups[0][i])
-          .select('g')
-          .attr('opacity', 1);
-      })
-      .on('mouseleave', function(currNode, i) {
-        d3.select(nodeEnter._groups[0][i])
-          .select('g')
-          .attr('opacity', 0);
       });
 
     nodeEnter
       .append('text')
-      .attr('dy', 12)
-      .attr('x', 70)
+      .attr('dy', -7)
+      .attr('x', 95)
+      .attr('stroke', '#333')
       .attr('text-anchor', function(d) {
         return 'start';
       })
       .text(function(d) {
-        return d.data.endTime ? d.data.endTime - d.data.startTime + ' ms' : d.data.traceId;
-      })
-      .on('click', (d, i) => {
-        this.showSpanModal(
-          d.data,
-          { width: '100%', top: -10, left: '0' },
-          d3.select(nodeEnter._groups[0][i]).append('rect')
-        );
-        d3.event.stopPropagation();
-      })
-      .on('mouseenter', function(currNode, i) {
-        d3.select(nodeEnter._groups[0][i])
-          .select('g')
-          .attr('opacity', 1);
-      })
-      .on('mouseleave', function(currNode, i) {
-        d3.select(nodeEnter._groups[0][i])
-          .select('g')
-          .attr('opacity', 0);
+        return d.data.endTime
+          ? d.data.endTime - d.data.startTime + ' ms'
+          : d.data.traceId;
       });
 
     nodeEnter
       .append('circle')
       .attr('class', 'node')
       .attr('r', 4)
-      .attr('cx', '150')
+      .attr('cx', '158')
       .style('fill', function(d) {
         return d._children ? '#8543e0aa' : '#fff';
       })
       .on('click', click);
 
-    var nodeUpdate = nodeEnter.merge(node);
+    this.nodeUpdate = nodeEnter.merge(node);
 
-    nodeUpdate
+    this.nodeUpdate
       .transition()
       .duration(600)
       .attr('transform', function(d) {
         return 'translate(' + d.y + ',' + d.x + ')';
       });
 
-    nodeUpdate
+    this.nodeUpdate
       .select('circle.node')
       .attr('r', 4)
-      .attr('cx', '156')
+      .attr('cx', '158')
       .style('fill', function(d) {
         return d._children ? '#8543e0aa' : '#fff';
       })
@@ -373,10 +375,12 @@ export default class TraceMap {
 
     function diagonal(s, d) {
       return `M ${s.y} ${s.x}
-      L  ${d.y + 158} ${d.x}`;
+      C ${s.y - 30} ${s.x}, ${d.y + 188} ${d.x},
+      ${d.y + 158} ${d.x}`;
     }
-    const that = this;
-    function click(d) {
+    function click(d, i) {
+      that.tip.hide(d, this);
+      that.timeTip.hide(d, that.timeUpdate._groups[0][i]);
       if (d.children) {
         d._children = d.children;
         d.children = null;
@@ -384,7 +388,9 @@ export default class TraceMap {
         d.children = d._children;
         d._children = null;
       }
+      that.updatexAxis(d);
       that.update(d);
+      d3.event.stopPropagation();
     }
   }
   getZoomBehavior(g) {
@@ -394,7 +400,9 @@ export default class TraceMap {
       .on('zoom', () => {
         g.attr(
           'transform',
-          `translate(${d3.event.transform.x},${d3.event.transform.y})scale(${d3.event.transform.k})`
+          `translate(${d3.event.transform.x},${d3.event.transform.y})scale(${
+            d3.event.transform.k
+          })`
         );
       });
   }
index 28f95a8..90093ff 100644 (file)
  * See the License for the specific language governing permissions and\r
  * limitations under the License.\r
  */\r
- :global(.trace-tree .node) {\r
+\r
+:global(.trace-tree) {\r
+  height: 1000px;\r
+}\r
+:global(.trace-tree .node) {\r
+  cursor: pointer;\r
+}\r
+:global(.trace-tree .node circle) {\r
+  stroke: #ddd;\r
+  stroke-width: 1.5px;\r
+}\r
+:global(.trace-tree .time-inner) {\r
+  cursor: pointer;\r
+}\r
+:global(.trace-tree .time-bg) {\r
+  fill: #fff;\r
   cursor: pointer;\r
 }\r
- :global(.trace-tree .node circle) {\r
-    stroke: #8543e0aa;\r
-    stroke-width: 1.5px;\r
+:global(.trace-tree .time:hover) {\r
+  :global(.time-bg){\r
+  fill: rgba(0, 0, 0, 0.1);\r
   }\r
+}\r
 :global(.trace-tree .node text) {\r
-    font: 12px sans-serif;\r
-    stroke-width: 0.5;\r
-  }\r
+  font-size: 12px;\r
+  font-family: 'Courier New', Courier, monospace;\r
+  stroke-width: 0.5;\r
+}\r
 :global(.trace-tree .node .block) {\r
-    width: 150px;\r
-    height: 34px;\r
-  }\r
+  width: 158px;\r
+  height: 40px;\r
+}\r
+:global(.trace-tree .node .content) {\r
+  width: 158px;\r
+  height: 20px;\r
+}\r
+:global(.trace-tree .node .service) {\r
+  width: 7px;\r
+  height: 41px;\r
+}\r
 :global(.trace-tree .link) {\r
-    fill: none;\r
-    stroke: #8543e055;\r
-    stroke-width: 2px;\r
-  }\r
+  fill: none;\r
+  stroke: #ddd;\r
+  stroke-width: 1.5px;\r
+}\r
 :global(.trace-tree .domain) {\r
-    opacity: 0;\r
-  }\r
-:global(.trace-tree .tooltip-box) {\r
-    fill: #333;\r
-    height: 20px !important;\r
-  }\r
+  opacity: 0;\r
+}\r
+:global(.d3-tip) {\r
+  line-height: 1;\r
+  padding: 6px;\r
+  background: rgba(0, 0, 0, 0.8);\r
+  color: #fff;\r
+  border-radius: 4px;\r
+  font-size: 12px;\r
+}\r
+\r
+/* Creates a small triangle extender for the tooltip */\r
+:global(.d3-tip:after) {\r
+  box-sizing: border-box;\r
+  display: inline;\r
+  font-size: 10px;\r
+  width: 100%;\r
+  line-height: 1;\r
+  color: rgba(0, 0, 0, 0.8);\r
+  content: '\25BC';\r
+  position: absolute;\r
+  text-align: center;\r
+}\r
+\r
+/* Style northward tooltips specifically */\r
+:global(.d3-tip.n:after) {\r
+  margin: -2px 0 0 0;\r
+  top: 100%;\r
+  left: 0;\r
+}\r