01 - D3 Selections

On commence par importer la librairie d3.

import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";

Construire son SVG à la main

const svg = d3.create("svg")
  .attr("width", 400)
  .attr("height", 400)
  .style("background-color", "tomato")
  .attr("viewBox", "0 0 400 400");

svg.append("circle")
  .attr("cx", 100)
  .attr("cy", 200)
  .attr("r", 10)
  .style("fill", "lightyellow");

svg.append("circle")
  .attr("cx", 200)
  .attr("cy", 200)
  .attr("r", 10)
  .style("fill", "lightyellow");

svg.append("circle")
  .attr("cx", 300)
  .attr("cy", 200)
  .attr("r", 10)
  .style("fill", "lightyellow");

// Astuce pour afficher un résultat intermédiaire, pas nécessaire en prod ;
// ne fonctionne que pour du statique (cloneNode ne copie pas les event listeners).
const manualSvg = svg.node().cloneNode(true);
display(manualSvg);

Ajoutons un peu d’interactivité

svg.selectAll("circle")
  .classed("item", true)
  .attr("cx", (d, i) => i * 100 + 100)
  .attr("cy", 200)
  // Deux façons d'écrire la même chose, équivalentes :
  .on("mouseenter", (e, d) => d3.select(e.currentTarget).style("fill", "lightgreen"))
  .on("mouseleave", (e, d) => e.currentTarget.style.fill = "white");

// Node vivant (non cloné), pour que les event listeners restent attachés une fois affichés.
const interactionSvg = svg.node();
display(interactionSvg);

On va sûrement gagner un prix avec ça, c’est du jamais vu.

Ajoutons du texte

Plutôt que d’ajouter le texte directement, on passe par un groupe (g). Dans certains cas, c’est plus simple pour positionner le texte.

const textSvg = d3.create("svg")
  .attr("width", 400)
  .attr("height", 400)
  .attr("viewBox", "0 0 400 400")
  .style("background-color", "tomato");

const g = textSvg.append("g")
  .style("transform", "translate(200px, 200px)");

g.append("circle")
  .attr("r", 100)
  .style("fill", "lightyellow");

g.append("text")
  .text("Hello patate!")
  .style("fill", "black")
  .attr("text-anchor", "middle")
  .attr("dominant-baseline", "middle")
  .attr("y", 140);
display(textSvg.node())

Plusieurs groupes

const multiTextSvgWidth = 800;

const multiTextSvg = d3.create("svg")
  .attr("width", multiTextSvgWidth)
  .attr("height", 400)
  .attr("viewBox", `0 0 ${multiTextSvgWidth} 400`)
  .style("background-color", "tomato");

const g1 = multiTextSvg.append("g")
  .style("transform", "translate(200px, 200px)");

g1.append("circle")
  .attr("r", 100)
  .style("fill", "lightyellow");

g1.append("text")
  .text("Pomme de terre")
  .style("fill", "black")
  .attr("text-anchor", "middle")
  .attr("dominant-baseline", "middle")
  .attr("y", 140);

const g2 = multiTextSvg.append("g")
  .style("transform", "translate(200px, 200px)");

g2.append("circle")
  .attr("r", 100)
  .style("fill", "lightyellow");

g2.append("text")
  .text("Patate")
  .style("fill", "black")
  .attr("text-anchor", "middle")
  .attr("dominant-baseline", "middle")
  .attr("y", 140);

const g3 = multiTextSvg.append("g")
  .style("transform", "translate(200px, 200px)");

g3.append("circle")
  .attr("r", 100)
  .style("fill", "lightyellow");

g3.append("text")
  .text("Parmentière")
  .style("fill", "black")
  .attr("text-anchor", "middle")
  .attr("dominant-baseline", "middle")
  .attr("y", 140);

multiTextSvg.selectAll("g")
  .style("transform", (d, i) => `translate(${i * width / 5 + 200}px, 200px) scale(0.8)`)
display(multiTextSvg.node())