Published
Edited
Aug 17, 2021
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
{
let margin = { top: 30, right: 40, bottom: 40, left: 50 };
// Some random points to make a path
let pathCoordinates = [
[0, 220],
[190, 240],
[200, 100],
[300, 110],
[400, 200],
[600, 200],];
let width = 600;
let height = 400;
// make svg
const svg = DOM.svg(width + margin.left + margin.right,
height + margin.bottom + margin.top)
const svgSel = d3.select(svg)
const sel = svgSel.append('svg')
.attr('transform', `translate(${margin.left},${margin.top})`)
// d3 scales
let x = d3
.scaleLinear()
.domain(d3.extent(pathCoordinates, (d, i) => d[0]))
.range([margin.left, width - margin.right]);
let y = d3
.scaleLinear()
.domain(d3.extent(pathCoordinates, (d, i) => d[1]))
.range([height - margin.bottom, margin.top]);
// Line that connects points in the pathCoordinates
const line = d3
.line()
.x((d) => x(d[0]))
.y((d) => y(d[1]));
const path = sel
.append("path")
.attr("d", line(pathCoordinates))
.style("fill", "none")
.style("stroke", "black");
// Get the length of the path
const l = path.node().getTotalLength();
// Add a point at the beginning of the path
let initialTime = 0 ; //this will keep track of the time when the person clicks the red dot
sel
.append("circle")
.attr("cx", path.node().getPointAtLength(0).x)
.attr("cy", path.node().getPointAtLength(0).y)
.attr("class", "move")
.attr("r",10)
.style("fill", "red")
.style("stroke", "white")
.on("click", ()=> {
initialTime=performance.now(); // record the time when the user clicked the circle.
start()});
// Tell the viewer to click
sel
.append("text")
.attr("x", path.node().getPointAtLength(0).x - 20)
.attr("y", path.node().getPointAtLength(0).y - 20)
.text("Click Me!!")
.style("fill", "blue");
// If you wish to reverse the animation to go from getPointAtLength(l) to getPointAtLength(0), use l-time as a parameter instead of time, and start from getPointAtLength(l).
function start() {
let time = (performance.now() - initialTime) / 20; // scaled time lapse, change 10 to something else to adjust the speed!
if (time < l) { // Move the circle until it reaches almost the end!
sel
.select(".move")
.attr("cx", path.node().getPointAtLength(time).x)
.attr("cy", path.node().getPointAtLength(time).y)
.attr("r", 5)
.style("fill", "black")
.style("stroke", "black");
window.requestAnimationFrame(start); // don't forget this!
}
else{ //When time>l, just move to the end point of the path, or do other cool stuff.
sel
.select(".move")
.attr("cx", path.node().getPointAtLength(l).x)
.attr("cy", path.node().getPointAtLength(l).y)
.attr("r", 5)
.style("fill", "black")
.style("stroke", "black")
.transition()
.attr("r", 10)
.style("fill", "red")
.style("stroke", "red")
;
}
}
return svg}
Insert cell
md`**Tree Example**`
Insert cell
{
let margin = { top: 30, right: 40, bottom: 40, left: 50 };

// This is some random tree from SK-Learn
const tree ={id:"0",children:[{id:"1",children:[{id:"3",children:[{id:"5",impurity:"0.0",samples:"11",value:"[0.20.]",class:"1",},{id:"6",children:[{id:"7",impurity:"0.0",samples:"5",value:"[6.0.]",class:"0",},{id:"8",impurity:"0.0",samples:"2",value:"[0.2.]",class:"1",},],value:4.5000,color:"#e99f46",impurity:"0.8112781244591328",samples:"7",},],value:97.0509,color:"#feeca9",impurity:"0.74959525725948",samples:"18",},{id:"4",children:[{id:"9",impurity:"0.0",samples:"3",value:"[5.0.]",class:"0",},{id:"10",impurity:"0.9418285354475157",samples:"25",value:"[25.14.]",class:"0",},],value:1.5000,color:"#ce3874",impurity:"0.9023932827949789",samples:"28",},],value:0.5000,color:"#ce3874",impurity:"1.0",samples:"46",split:"2"},{id:"2",impurity:"0.0",samples:"7",value:"[9.0.]",class:"0"},],name:"NumberofColors",value:3.5000,color:"#005da3",impurity:"0.9910760598382222",samples:"53",split:"1",};
let width = 600;
let height = 500;
const squareSize = 100;
const squareCol = "#de3d83";
const squareBgd = "red";
const strokeCol = "#fff";
const strokeWidth = 1;
const treeSize = 300;
const root = d3.hierarchy(tree, (d) => d.children);
const d3Tree = d3.tree().size([treeSize * 1.2, treeSize * 1]);
const treeData = d3Tree(root);
// make svg
const svg = DOM.svg(width + margin.left + margin.right,
height + margin.bottom + margin.top)
const svgSel = d3.select(svg)
const sel = svgSel.append('g').attr('transform', `translate(${margin.left},${margin.top}))`);
// These will hold path, length and time.
let path0;
let path3;
let path4 ;
let path6;
let path;
let l;
let initialTime;
const yScale = d3.scaleLinear().domain([0,height]).range([margin.top*2,height-(margin.top)]);
drawTree(sel);
// This method draws a simple tree in desinated g element
function drawTree(svgLocation) {
let rScale = d3.scaleSequential().domain([0, 1]).range([0, 4]);
// draw links
const cell = svgLocation;
const treePaths = cell
.append("g")
.attr("class", "tree-link-g")
.selectAll("path")
.data(treeData.links())
.join("path")
.attr(
"d",
d3
.linkVertical()
.x((d) => d.x)
.y((d) => yScale(d.y))
)
.attr("class", (d,i) => `link${i}`)
.attr("stroke", "#354848")
.attr("stroke-width", 0.75 * strokeWidth);

// draw nodes
const treeNodes = cell
.append("g")
.attr("class", "tree-node-g")
.selectAll("rect")
.data(treeData.descendants());
const rectSize = rScale(treeSize / 50);
const treeCircles = treeNodes
.join("rect")
.attr("fill", "rgb(206, 56, 116)")
.attr("height", rectSize)
.attr("width", rectSize)
.attr("rx", 1)
.attr("ry", 1)
.attr("x", (d) => d.x - rectSize / 2)
.attr("y", (d) => yScale(d.y) - rectSize / 2)
.attr("stroke", "#354848")
.attr("stroke-width", strokeWidth / 2)
.transition() // This is more like a "HACK" to make the stuff below runs only after the tree is drawn. It's observable specific.
.on("end", (d,i)=>{
if(i===0) {
// Finally, let's pick a path that an object must follow!
//link0->link2->link5->link8 Change this sequence and see what happens!!
path0 = d3.select(".link0").node();
path3 = d3.select(".link2").node();
path4 = d3.select(".link5").node();
path6 = d3.select(".link8").node();
path = d3.select(".tree-link-g").append("path").attr("class","route").attr("opacity",0);
path.attr("d",
`${path0.getAttribute("d")} ${path3.getAttribute("d")} ${path4.getAttribute("d")} ${path6.getAttribute("d")}`)
// Get the length of the path
l = path.node().getTotalLength();
// Below are the same code as the simple path example.
// Add a point at the beginning of the path
initialTime = 0 ; //this will keep track of the time when the person clicks the red dot
sel
.append("circle")
.attr("cx", path.node().getPointAtLength(0).x)
.attr("cy", path.node().getPointAtLength(0).y)
.attr("class", "move")
.attr("r",20)
.style("fill", "red")
.style("stroke", "white")
.on("click", ()=> {
initialTime=performance.now(); // record the time when the user clicked the circle.
start()});
// Tell the viewer to click
sel
.append("text")
.attr("x", path.node().getPointAtLength(0).x + 20)
.attr("y", path.node().getPointAtLength(0).y + 20)
.text("Click Me!!")
.style("fill", "blue");

}});;;
}

// Animation Function. Use (l- time) as a parameter to reverse the animation that goes from getPointAtLength(l) to getPointAtLength(0)!! (And don't forget to start from the end, getPointAtLength(l)!)
function start() {
let time = (performance.now() - initialTime) / 20; // scaled time lapse, change 10 to something else to adjust speed!
if (time < l) { // Move the circle until it reaches almost the end!
sel
.select(".move")
.attr("cx", path.node().getPointAtLength(time).x)
.attr("cy", path.node().getPointAtLength(time).y)
.attr("r", 10)
.style("fill", "black")
.style("stroke", "black");
window.requestAnimationFrame(start); // don't forget this!
}
else{ //When time>l, just move to the end point of the path, or do other cool stuff.
sel
.select(".move")
.attr("cx", path.node().getPointAtLength(l).x)
.attr("cy", path.node().getPointAtLength(l).y)
.attr("r", 5)
.style("fill", "black")
.style("stroke", "black")
.transition()
.attr("r", 20)
.style("fill", "red")
.style("stroke", "red")
;
}
}
return svg}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more