Sortable bar Chart s D3.js

Tento graf je vytvořen na základě vzorového kódu Mikea Bostocka:https://observablehq.com/@d3/activate-bar-chart#chart

Přepsal jsem kód implementující svou vlastní logiku. Než se vrhneme na kroky vytváření grafu, pojďme rychle pochopit data za tímto kódem.

Toto je malá datová sada s abecedami a jejich četností použití ve slovech.

Zde je odkaz na datová sada.

Komponenty, které mají být přidány:

  • Přidejte kontejner DIV, který obsahuje prvek SVG a jeho obsah
  • Přidejte rozevírací výběrové pole, které vám umožní vybrat možnosti řazení pruhů
  • Nakonec přidejte prvek SVG na místo kreslení grafu

Zde je kompletní kód pro vytvoření tohoto grafu. Vysvětlím každou část, jak a kdy to bude potřeba.

Soubor HTML: index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <link rel="stylesheet" href="style.css" />
    <title>Sorted Bar Charts</title>
  </head>
  <body>
    <h1 style="text-align: center">Sortable Bar charts</h1>
    <p style="text-align: center">
      This chart implements bar chart sorting with D3.js.
    </p>
    <div id="selectMe"></div>
    <div id="chartContainer"></div>
    <script src="script.js"></script>
  </body>
</html>

Pokud si všimnete v tomto souboru HTML, vytvořil jsem dva DIV. Jeden drží rozevírací seznam a druhý prvek SVG.

Soubor CSS: style.css

.label {
  margin-left: 15px;
}

#selection {
  font-size: 19px;
  margin-left: 15px;
}

Tento soubor CSS stylizuje rozbalovací nabídku a její popisek.

Soubor JavaScript: script.js

const w = 1000;
const h = 500;

const margins = { top: 50, left: 100, bottom: 100, right: 100 }
const innerWidth = w - margins.left - margins.right;
const innerHeight = h - margins.top - margins.bottom;

const url = "https://gist.githubusercontent.com/learning-monk/96c71857dcfc348ecbcda5cb4a90163a/raw/69fa895f7b5f2d99d236762969c46b1a9c47604a/alphabet.csv";

d3.csv(url).then((data, error) => {
  if (error) throw error;
  // console.log(data);

  data.forEach(d => {
    d.letter = d.letter;
    d.frequency = +d.frequency;
  });

  // Create a select dropdown
  const mySelection = document.getElementById("selectMe");

  d3.select(mySelection).append("span").append("p").attr("class", "label").text("How should these bars sorted?").style("font-weight", "bold").style("color", "red").style("font-size", "25px");

  const selectItems = ["Alphabetically", "Ascendingly", "Descendingly"];

  // Create a drop down
  d3.select(mySelection)
    .append("span")
    .append("select")
    .attr("id", "selection")
    .attr("name", "tasks")
    .selectAll("option")
    .data(selectItems)
    .enter()
    .append("option")
    .attr("value", d => d)
    .text(d => d);

  // When the page loads, the chart which sorted alphabetically loads by default
  document.addEventListener("DOMContentLoaded", myChart()); 


  // Chart changes based on drop down selection
  d3.select("#selection").on("change", function() {
    const selectedOption = d3.select(this).node().value;
    if (selectedOption == "Ascendingly") {
      data.sort((a,b) => {
        return d3.ascending(a.frequency, b.frequency)
      }) 
    } else if (selectedOption == "Descendingly") {
      data.sort((a,b) => {
        return d3.descending(a.frequency, b.frequency)
      })
    } else if (selectedOption == "Alphabetically") {
      data.sort((a,b) => {
        return d3.ascending(a.letter, b.letter)
      })
    }
    myChart();
  })

  function myChart () {
    // Append SVG to this DIV
    const chartDIV = document.createElement("div");

    // Create scales
    const xScale = d3.scaleBand()
    .domain(data.map((d) => d.letter))
    .rangeRound([0, innerWidth])
    .paddingInner(0.05);

    const yScale = d3.scaleLinear()
      .domain([0,d3.max(data, d => d.frequency)]).nice()
      .range([innerHeight, 0]);

    const xAxis = d3.axisBottom().scale(xScale);

    const yAxis = d3.axisLeft().scale(yScale);

    const svg = d3.select(chartDIV)
      .append("svg")
      .attr("viewBox", [0,0,w,h]);

    const mainG = svg
      .append("g")
      .attr("transform", `translate(${margins.left}, ${margins.top})`);

    const g = mainG
      .selectAll("g")
      .data(data)
      .enter()
      .append("g")
      .attr("transform", `translate(15,0)`);

    g.append("rect")
      .attr("class", "bars")
      .attr("x", d => xScale(d.letter) - innerWidth/data.length/2)
      .attr("y", d => yScale(d.frequency))
      .attr("width", innerWidth/data.length-1.5)
      .attr("height", (d) => innerHeight - yScale(d.frequency))
      .attr("fill", d => d.frequency == d3.max(data, d => d.frequency) ? "#f4c430" : "green")
      .append("text")
        .attr("x", 5*3)
        .attr("y", (d,i) => i*5)
        .text(d => d.frequency);

    mainG
      .append("g")
      .call(xAxis)
      .attr("transform", `translate(0, ${innerHeight})`);

    mainG
      .append("g")
      .call(yAxis);

    // This code will redraw charts based on dropdown selection. At any point in time, chartContainer DIV only contains one chart. The charts are recycled.
    const showChart = document.getElementById("chartContainer");
    while (showChart.firstChild) {
      showChart.firstChild.remove();
    }
    showChart.appendChild(chartDIV);

  }

});

Tento kód D3.js lze široce rozdělit do bloků, jak je uvedeno níže.

  • Načíst data
  • Transformujte data
  • Vytvořte rozbalovací nabídku s hodnotami
  • Vytvořte měřítka
  • Vytvořte SVG
  • Přidat prvky grafu do SVG
  • Připojit SVG ke kontejneru grafu DIV
  • Zapsat funkce pro vykreslení grafů pro Načíst a Změnit události.

Většina výše uvedených kroků je společná pro grafy D3.js. Nejdu tedy do hloubky každého kroku. Chci však zvláště zdůraznit Načíst a Změnit události spojené s tímto grafem.

Když se graf načte, zobrazí se výchozí graf, tj. pruhy v abecedním pořadí.

document.addEventListener("DOMContentLoaded", myChart());

myChart() je funkce, která je šablonou pro kreslení grafu.

Když uživatel změní výběr z rozbalovací nabídky, vykreslí se grafy založené na výběru. V tomto případě Změnit akce začíná.

   d3.select("#selection").on("change", function() {
      const selectedOption = d3.select(this).node().value;
      if (selectedOption == "Ascendingly") {
        data.sort((a,b) => {
          return d3.ascending(a.frequency, b.frequency)
        }) 
      } else if (selectedOption == "Descendingly") {
        data.sort((a,b) => {
          return d3.descending(a.frequency, b.frequency)
        })
      } else if (selectedOption == "Alphabetically") {
        data.sort((a,b) => {
          return d3.ascending(a.letter, b.letter)
        })
      }
     myChart();
   })

Tato část kódu třídí podkladová data na základě výběru uživatele a tato data jsou předávána do myChart() nakreslit graf.

Recyklace probíhá na konci kódu, kde se SVG spolu s prvky grafu mění na základě výběru uživatele. Podobně jako u tabule, kde se předchozí poznámky vymažou a nové poznámky se načrtnou.

Doufám, že vám tento článek pomohl pochopit logiku řazení sloupců ve sloupcovém grafu.

Pokud máte nějaké dotazy, neváhejte se na mě obrátit.