Skip to content

Instantly share code, notes, and snippets.

@ravi4j
Created July 23, 2017 00:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ravi4j/9d4871f11fa959fa6ded19a30d4b3227 to your computer and use it in GitHub Desktop.
Save ravi4j/9d4871f11fa959fa6ded19a30d4b3227 to your computer and use it in GitHub Desktop.
Binary Tree : D3 + Vue.js
<!DOCTYPE html>
<head>
<title>Welcome to Vue</title>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/vue"></script>
<style>
body {
width: 100%;
height: 100%;
font-family: monospace;
}
.controls {
position: fixed;
top: 16px;
left: 16px;
background: #f8f8f8;
padding: 0.5rem;
display: flex;
flex-direction: column;
}
.svg-container {
display: table;
}
#svgId {
background-color: #fff;
}
.controls>*+* {
margin-top: 1rem;
}
label {
display: block;
}
.node {
opacity: 1;
}
.node circle {
fill: #999;
cursor: pointer;
}
.node text {
font: 10px sans-serif;
cursor: pointer;
}
.node--internal circle {
fill: #555;
}
.node--internal text {
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
stroke-dasharray: 1000;
}
.node:hover {
pointer-events: all;
stroke: #ff0000;
}
.node.highlight {
fill: red;
}
</style>
</head>
<body>
<div id="app">
<div class="controls">
<div>
<label>Adjust width</label>
<input type="range" v-model="settings.width" min="0" max="100" />
</div>
<div>
<label>Stroke color</label>
<input type="color" v-model="settings.strokeColor" />
</div>
<div>
Selected: {{ selected }}
</div>
</div>
<div class="svg-container" :style="{width: settings.width + '%'}">
<svg id="svgId" viewBox="0 0 960 600" preserveAspectRatio="xMinYMin meet">
<transition-group tag="g" name="line">
<path v-for="link in links" class="link" v-bind:key="link.id" v-bind:d="link.d" v-bind:style="link.style"></path>
</transition-group>
<transition-group tag="g" name="list">
<g class="node" v-on:click="select(index, node)" v-for="(node, index) in nodes" v-bind:key="node.id" v-bind:style="node.style">
<circle v-bind:r="node.r" v-bind:style="{'fill': index == selected ? '#ff0000' : '#bfbfbf'}"></circle>
<text v-bind:dx="node.textpos.x" v-bind:dy="node.textpos.y" v-bind:style="node.textStyle">{{ node.text }}</text>
</g>
</transition-group>
</svg>
</div>
</div>
<script type="text/javascript">
new Vue({
el: "#app",
data: function () {
return {
csv: null,
selected: null,
settings: {
strokeColor: "#29B5FF",
width: 100
}
};
},
mounted: function () {
var that = this;
d3.csv("tree-data.csv",
function (error, data) {
if (error) throw error;
that.csv = data;
}
);
},
computed: {
root: function () {
if (this.csv) {
var root = d3.stratify()
.id(function (d) { return d.node; })
.parentId(function (d) { return d.parent; })
(this.csv);
return this.tree(root);
}
},
tree: function () {
return d3
.tree()
.size([600, this.settings.width - 300]);
},
nodes: function () {
var that = this;
if (this.root) {
return this.root.descendants().map(function (d) {
let x = (200 + d.x) + "px";
let y = parseInt(-1 * d.y + 30) + "px";
return {
id: d.id,
r: 2.5,
text: d.id,
style: {
transform: "translate(" + x + "," + y + ")"
},
textpos: {
x: d.children ? -8 : 8,
y: 3
},
textStyle: {
textAnchor: d.children ? "end" : "start"
}
};
});
}
},
links: function () {
let that = this;
if (this.root) {
return this.root.descendants().slice(1).map(function (d) {
let x = d.x + 200, parent_x = d.parent.x + 200;
let y = parseInt(-1 * d.y + 30);
let parent_y = parseInt(-1 * d.parent.y + 30);
return {
id: d.id,
d: "M" + x + "," + y + "L" + parent_x + "," + parent_y,
style: {
stroke: that.settings.strokeColor
}
};
});
}
}
},
methods: {
select: function (index, node) {
this.selected = index;
}
}
});
</script>
</body>
node parent
A
B A
C A
D B
E B
F C
G C
H D
I D
J E
K E
L F
M F
N G
O G
P H
Q H
R I
S I
T J
U J
V K
W K
X L
Y L
Z M
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment