Published
Edited
Jan 1, 2021
Insert cell
Insert cell
chart = {
const width = 200;
const height = 140;

const svg = d3.select(DOM.svg(width, height))
.style("width", "100%")
.style("height", "auto");
// Pointe de flèche grise
const marker = svg.append('defs')
.append("marker")
.attr("id", "arrowhead")
.attr("markerWidth", 8)
.attr("markerHeight", 8)
.attr("refX", 6)
.attr("refY", 3)
.attr("orient", "auto")
.append('path')
.attr("d", "M 0 0 L 6 3 L 0 6")
.attr("fill", "none")
.attr("stroke", "grey")
.attr("stroke-linejoin", "miter-clip");
const dr = rayon / 2;
const decalage = rayon + 4;
const formes = svg.append("svg:g")
.attr("id", "forme");
// Cadre
formes
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1);
// Points attirés
const pt = formes.selectAll("circle").data(pos).enter()
.append("circle")
.attr("cx", function(d) {return d.x;})
.attr("cy", function(d) {return d.y;})
.attr("r", rayon)
.attr("fill", "black")
.attr("stroke-width", 1)
.attr("stroke", "black")
.transition()
.on("start", function repeat() {
d3.active(this)
.transition().duration(vitesse)
.attr("fill", "white")
.attr("stroke", "grey")
.transition().duration(0)
.attr("fill", "black")
.attr("stroke", "black")
.on("end", repeat);
});
// Pôle attractif
const ptpole = formes
.append("circle")
.attr("cx", pole.x)
.attr("cy", pole.y)
.attr("r", rayon)
.attr("fill", "white")
.attr("stroke-width", 1)
.attr("stroke", "black")
.transition()
.on("start", function repeat() {
d3.active(this)
.transition().duration(vitesse)
.attr("fill", "black")
.attr("stroke", "black")
.transition().duration(0)
.attr("fill", "white")
.attr("stroke", "grey")
.on("end", repeat);
});
// Flèches en tiretés avec pointe
const LFleche = [];
for (const dest in pos) {
LFleche.push(offset(pos[dest], pole, decalage));
}

const fleche = formes.selectAll("line").data(pos).enter()
.append("line")
.attr("x1", function(d,i) {return LFleche[i][0].x;})
.attr("y1", function(d,i) {return LFleche[i][0].y;})
.attr("x2", function(d,i) {return LFleche[i][1].x;})
.attr("y2", function(d,i) {return LFleche[i][1].y;})
.attr("stroke-width", 1)
.attr("stroke", "grey")
.attr("stroke-dasharray", "4, 2")
.attr("marker-end", "url(#arrowhead)");
// Points qui se déplacent
const pm = formes.selectAll("div").data(pos).enter()
.append("circle")
.attr("cx", function(d) {return d.x;})
.attr("cy", function(d) {return d.y;})
.attr("r", rayon)
.attr("fill", "black")
.transition()
.on("start", function repeat() {
d3.active(this)
.transition().duration(vitesse)
.attr("cx", pole.x)
.attr("cy", pole.y)
.transition().duration(0)
.attr("cx", function(d) {return d.x;})
.attr("cy", function(d) {return d.y;})
.on("end", repeat);
});
//Dessin par rendu
return svg.node();
}
Insert cell
//Vitesse de l'animation
vitesse = 2000;
Insert cell
rayon = 4;
Insert cell
// Position du pôle attractif
pole = ({"x": 80, "y": 40});
Insert cell
pos = [{x: 20, y: 10},
{x: 40, y: 80},
{x: 120, y: 100}];
Insert cell
offset = function(start,destination,decalage) {
var dx = parseInt(destination.x) - parseInt(start.x);
var dy = parseInt(destination.y) - parseInt(start.y);
var angle = Math.atan2(dy,dx);
var newStart = {"x": start.x - Math.cos(angle-Math.PI)*decalage, "y": start.y - Math.sin(angle-Math.PI)*decalage};
var newDestination = {"x": destination.x + Math.cos(angle-Math.PI)*decalage, "y": destination.y + Math.sin(angle-Math.PI)*decalage};
;
return [newStart,newDestination]
}
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more