5 zásadních konceptů pro výuku d3.js a jak jim porozumět

Možná jste již slyšeli o d3.js , oslnivá knihovna JavaScript, která vám umožní vytvářet nádherné grafy a grafiku pomocí pouhých několika řádků kódu. Možná jste viděli některé z fantastických příkladů D3 v akci, nebo jste možná slyšeli, že New York Times ji používá k vytváření svých interaktivních vizuálních příběhů.

Pokud jste se někdy pokusili ponořit do světa D3, pak už budete obeznámeni s jeho slavně strmou křivkou učení .

S D3 prostě nemůžete stavět věci hned po vybalení.

Se svými matoucími řetězci metod, mimozemskou syntaxí a funkcemi black-box, které vypadají, že fungují jako kouzlo, se D3 může rychle jevit jako větší problém, než stojí za to. Ale nebojte se, protože D3 bude podstatně jednodušší, pokud pochopíte jen několik klíčových pojmů.

Chci vás provést jednoduchým tutoriálem vysvětlujícím 5 nejčastějších oblastí zmatku, se kterými se začátečníci setkávají, když začínají s D3.

Vytvoříme dynamický bodový graf, který se aktualizuje každou sekundu mezi dvěma různými sadami dat:

Udělejte si chvilku na posouzení malých detailů zde. Podívejte se, jak hladce se tyto tečky posouvají po obrazovce. Podívejte se, jak jemně mizí a mizí z pohledu. Podívejte se na klidné houpání našich os mezi jejich různými hodnotami.

Toto jsou ve skutečnosti některé z nejjednodušších funkcí, které lze v D3 implementovat. Jakmile se dostanete přes počáteční boj s určením základních stavebních kamenů knihovny, přidání tohoto druhu věcí je hračka.

Než předběhneme, promluvme si o tom, co D3 vlastně je.

D3 je zkratka pro Data Driven Documents .

data může být naprosto cokoliv, což je součástí toho, co dělá D3 tak výkonným. Většinu času v D3 budete chtít načíst tato data ze souboru, ale pro tento příklad použijeme pouze dvě pole uložená jako proměnné:

var data0 = [
        { gpa: 3.42, height: 138 },
        { gpa: 3.54, height: 153 },
        { gpa: 3.14, height: 148 },
        { gpa: 2.76, height: 164 },
        { gpa: 2.95, height: 162 },
        { gpa: 3.36, height: 143 }
]

var data1 = [
    { gpa: 3.15, height: 157 },
    { gpa: 3.12, height: 175 },
    { gpa: 3.67, height: 167 },
    { gpa: 3.85, height: 149 },
    { gpa: 2.32, height: 165 },
    { gpa: 3.01, height: 171 },
    { gpa: 3.54, height: 168 },
    { gpa: 2.89, height: 180 },
    { gpa: 3.75, height: 153 }
]

Dokumenty část v D3 odkazuje na objektový model dokumentu (DOM). D3 je o přesouvání prvků na stránce na základě toho, co říkají data. Konkrétně pracujeme se speciálními tvarovými prvky nazývanými SVG.

Zásadní koncept #1 — Práce s SVG

Zde se tedy dostáváme k prvnímu náročnému konceptu, se kterým se musí každý nováček D3 vypořádat. Okamžitě se musíte dobře zorientovat ve speciálním typu označení, které jste možná ještě neviděli.

Takto může vypadat označení SVG:

<svg width="400" height="60">
  <rect x="0" y="0" width="50" height="50" fill="green"></rect>
  <circle cx="90" cy="25" r="25" fill="red"></circle>
  <ellipse cx="145" cy="25" rx="15" ry="25" fill="grey"></ellipse>
  <line x1="185" y1="5" x2="230" y2="40" stroke="blue" stroke-width="5"></line>
  <text x="260" y="25" font-size="20px" fill="orange">Hello World</text>
</svg>

Pokud tento úryvek vložíme do dokumentu HTML, náš prohlížeč jej bude interpretovat takto:

V zásadě má každý z těchto SVG sadu atributů, které náš prohlížeč používá k umístění těchto tvarů na obrazovku. Pár věcí, které byste měli vědět o SVG:

  • Existuje rozdíl mezi SVG plátnem (nakresleným pomocí značek ) a samotnými tvary SVG.
  • Je zde poměrně neintuitivní systém souřadnic, kterému musíte porozumět, protože bod (0, 0) mřížky SVG je vlevo nahoře, nikoli vlevo dole.
  • Pokud nerozumíte tomu, co se děje pod kapotou, můžete narazit na docela podivné chování.

Může být lákavé toto téma ignorovat a místo toho se rozhodnout, že se po hlavě ponoříme do vzrušujícího podnikání, kterým je okamžité stanovení nějakého kódu D3, ale věci se budou zdát mnohem jasnější později, pokud budete vědět, jak tyto tvary fungují.

Zdroje pro pochopení SVG…

  • Průvodce SVG pro úplné začátečníky — Rob Levin
  • Základní nátěr SVG pro D3 – Scott Murray

Jako první krok k vytvoření našeho bodového grafu budeme chtít přidat malý kruh SVG pro každou položku dat, kterou chceme zobrazit. SVG přidáváme do D3 takto:

d3.select("#canvas")
    .append("circle")
        .attr("cx", 50)
        .attr("cy", 50)
        .attr("r", 5)
        .attr("fill", "grey");

Zápis d3.select(“#canvas”) zde je analogie psaní $(“#canvas”) v jQuery, když uchopí prvek s ID „canvas“. d3.select jde ještě o krok dále a přidává k tomuto výběru několik speciálních metod, které použijeme později.

Používáme d3.append způsob přidání kruhu SVG k tomuto prvku a každému z atributů kruhu nastavíme d3.attr metoda.

Protože chceme přidat kruh pro každou položku v našem poli, možná si myslíte, že bychom chtěli použít cyklus for:

for(var i = 0; i < data0.length; i++) {
    d3.select("#canvas")
        .append("circle")
            .attr("cx", data0[i].gpa)
            .attr("cy", data0[i].height)
            .attr("r", 5)
            .attr("fill", "grey");
}

Protože se však jedná o D3, uděláme něco trochu složitějšího a trochu výkonnějšího…

Zásadní koncept #2 — Data Binding

Další překážkou, kterou musí každý nový vývojář D3 překonat, je spojení dat D3. D3 má svůj vlastní speciální způsob vazby dat na naše SVG.

Zde je návod, jak přidáme kruh pro každou položku v našem poli pomocí D3:

var circles = d3.select("#canvas").selectAll("circle")
    .data(data0);

circles.enter().append("circle")
    .attr("cx", function(d, i){ return 25 + (50 * i); })
    .attr("cy", function(d, i){ return 25 + (50 * i); })
    .attr("r", 5)
    .attr("fill", "grey");

Pro vývojáře, který s D3 teprve začíná, se to může zdát matoucí. Ve skutečnosti to pro mnoho zkušených vývojářů s letitými zkušenostmi v D3 může stále vypadat matoucí…

Mysleli byste si, že volání selectAll(“kruh”) na stránce bez kruhů vrátí výběr ničeho. Poté zavoláme metodu data() na tento výběr ničeho a předáme v našem poli. Máme záhadné volání metody enter() a pak máme podobné nastavení jako předtím.

Tento blok kódu přidává kruh pro každou položku v našem poli, což nám umožňuje nastavit naše atributy pomocí anonymních funkcí. První argument těchto funkcí nám poskytuje přístup k položce v našich datech, na kterou se díváme, a druhý argument nám poskytuje index položky v našem poli.

Vytvoření „spojení dat“, jako je toto, představuje první krok k tomu, abychom s našimi daty udělali něco užitečného, ​​takže je to důležitý krok k pochopení. Tato podivná syntaxe může být skličující, když se s ní poprvé setkáte, ale je to užitečný nástroj, abyste věděli, jak ji používat.

Zdroje pro pochopení datové vazby v D3:

  • Průvodce datovou vazbou pro začátečníky – SitePoint
  • Myšlení s připojením — Mike Bostock
  • Pojďme vytvořit mřížku pomocí D3.js – Chuck Grimmett

Jakmile spustíme kód, který jsme dosud napsali, skončíme s něčím, co vypadá takto:

Na obrazovku jsme připojili správný počet kruhů a trochu je rozmístili, ale to, co máme zatím, není nijak zvlášť užitečné. Pro bodový graf by souřadnice těchto kružnic měly odpovídat dvěma různým hodnotám.

Hodnoty GPA a výšky, které máme v našich polích, nám v tuto chvíli příliš nepomáhají. Naše hodnoty GPA se pohybují od 2,32 do 3,85 a naše hodnoty výšky se pohybují od 138 do 180. Při umístění našich kruhů chceme pracovat s hodnotami x mezi 0 a 800 (šířka našeho SVG) a hodnotami y mezi 0 a 500 (výška našeho SVG).

Na naše nezpracovaná data budeme muset použít nějakou transformaci, abychom tyto hodnoty převedli do formátu, který můžeme použít.

V D3 to děláme pomocí měřítek.

Zásadní koncept #3 — váhy

Zde přichází naše další velká výzva k vyzvednutí D3.

Váhy jsou matoucí, když se o nich mluví, když začínáte. Musí jim být nastavena doména a rozsah , což lze docela snadno splést. doména představuje interval, který zadáváme hodnoty bude probíhat mezi a rozsahem představuje interval, ve kterém jsou naše výstupní hodnoty poběží mezi.

Stupnice je funkce v D3, která vezme hodnotu jako vstup a vyplivne jinou hodnotu jako výstup. V tomto příkladu budeme potřebovat měřítko x, které převede GPA na hodnotu pixelu, a měřítko y, které převede výšku osoby na hodnotu pixelu, abychom mohli použít naše data k nastavení atributů našich kruhů. .

Zde je diagram, který vám ukáže, co by naše měřítko x mělo dělat:

Musíme inicializovat naši doménu a rozsah s nějakými minimálními a maximálními hodnotami. Říkáme, že hodnota 3,54 by se měla převést na hodnotu pixelu 800 a GPA 2,76 by se měla převést na hodnotu pixelu 0. Pokud tedy do naší stupnice předáme hodnotu 3,15, výstup by být 400, protože 3.15 je v polovině mezi minimem a maximem naší domény.

V tomto příkladu používáme lineární měřítko, což znamená, že hodnoty by měly být škálovány proporcionálně mezi dvěma extrémy, na které se díváme. Existuje však několik různých typů vah, které si budete chtít vyzkoušet.

  • Pokud pracujete s daty, která v průběhu času exponenciálně narůstají, možná budete chtít použít logaritmickou stupnici .
  • Pokud pracujete s hodnotami data, použijete časové měřítko .
  • Pokud chcete přiřadit barvy mezi různé kategorie, můžete použít ordinální stupnici .
  • Pokud vytváříte mezery mezi obdélníky ve sloupcovém grafu, použijete pásmovou stupnici .

Pro každou z těchto stupnic se syntaxe mírně liší, ale stále bude mít stejný obecný formát jako naše lineární stupnice.

Zdroje pro pochopení měřítek v D3…

  • Úvod do lineárních měřítek v D3 — Ben Clikinbeard
  • Návod k různým typům měřítek – D3 do hloubky
  • Položka pro měřítka v referenci rozhraní D3 API

Nyní tedy můžeme přidat dvě lineární měřítka, která použijeme pro naše osy x a y.

var x = d3.scaleLinear()
    .domain([d3.min(data0, function(d){ return d.gpa; }) / 1.05, 
        d3.max(data0, function(d){ return d.gpa; }) * 1.05])
    .range([0, 800]);

var y = d3.scaleLinear()
    .domain([d3.min(data0, function(d){ return d.height; }) / 1.05,
        d3.max(data0, function(d){ return d.height; }) * 1.05])
    .range([500, 0]);

Každá z našich vah bude mít hodnotu někde mezi minimem a maximem každé proměnné v našich datech a vyplivne hodnotu pixelu, kterou můžeme použít pro naše SVG. Používám zde funkce d3.min() a d3.max(), takže D3 se automaticky přizpůsobí, pokud se naše datová sada změní. Také dávám našim doménám 5% vyrovnávací paměť v obou směrech, aby se všechny naše tečky vešly na obrazovku.

Také obracíme hodnoty rozsahu pro naši stupnici y, protože vstup 0 by měl vyplivnout výstup 500 pixelů (spodní část kartézské mřížky v souřadnicovém systému SVG).

Dále můžeme provést několik úprav našeho kódu z dřívější doby, aby hodnoty pro naše kruhy vycházely z našich vah.

var circles = d3.select("#canvas").selectAll("circle")
    .data(data0);

circles.enter()
    .append("circle")
        .attr("cx", function(d){ return x(d.gpa) })
        .attr("cy", function(d){ return y(d.height) })
        .attr("r", 5)
        .attr("fill", "grey");

V tuto chvíli máme něco, co vypadá jako skutečná vizualizace!

Dalším krokem je přidání některých os, abychom mohli říci, co mají tyto tečky představovat. Můžeme to udělat pomocí funkcí generátoru os D3, ale brzy narazíme na nějaké problémy…

Zásadní koncept #4 – okraje a osy

Generátory os D3 fungují tak, že připojují osu ke kterémukoli prvku, na který jsou volány. Problém je v tom, že když se pokusíme připevnit osy přímo na naše plátno SVG, skončíme s něčím takovým:

Náš první problém je, že osy jsou vždy umístěny v levém horním rohu mřížky. To je v tomto případě v pořádku pro naši osu y, ale není to v pořádku pro naši osu x, kterou chceme umístit dole.

Dalším problémem je, že protože naše osy vyčnívají přes okraj našeho plátna SVG, naše značky os se nezobrazují na naší ose y.

Můžeme to napravit použitím několika skupin SVG – neviditelných prvků pro přidání struktury na naše stránky.

V D3 si musíme zvyknout na „konvenci o rozpětí“, kterou by všechny naše projekty měly dodržovat:

Myšlenka je taková, že si chceme vytvořit nárazník kolem okraje naší vizualizační oblasti, který nám poskytne určitý prostor pro život našich os. Potřebujeme nastavit některé proměnné šířky, výšky a okraje v horní části našeho souboru, což nám umožní simulovat tento efekt:

ar svg = d3.select("#canvas");

var margin = {top: 10, right: 10, bottom: 50, left: 50};
var width = +svg.attr("width") - margin.left - margin.right;
var height = +svg.attr("height") - margin.top - margin.bottom;

var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

Nyní potřebujeme tyto proměnné šířky a výšky použít k nastavení rozsahu pro naše měřítka a k této proměnné g, která představuje naši hlavní oblast vizualizace, připojíme naše kruhy.

Pokud také připojíme naše osy ke skupinám SVG, můžeme je posunout do správné polohy pomocí atributu transform, který je součástí prvku group. Zde je kód, který použijeme k přidání našich os do našeho grafu:

// Axes
var xAxisCall = d3.axisBottom(x)
var xAxis = g.append("g")
    .attr("class", "x-axis")
    .attr("transform", "translate(" + 0 + "," + height + ")")
    .call(xAxisCall);

var yAxisCall = d3.axisLeft(y)
var yAxis = g.append("g")
    .attr("class", "y-axis")
    .call(yAxisCall)

// Labels
xAxis.append("text")
    .attr("class", "axis-title")
    .attr("transform", "translate(" + width + ", 0)")
    .attr("y", -6)
    .text("Grade Point Average")
yAxis.append("text")
    .attr("class", "axis-title")
    .attr("transform", "rotate(-90)")
    .attr("y", 16)
    .text("Height / Centimeters");

Přidávám také několik textových SVG jako štítky, které nám řeknou, co každá z os zobrazuje.

Konvence okrajů se pro nováčky v D3 může zdát trochu náhodná a existuje široká škála metod, které můžeme použít k úpravě toho, jak by naše značky měly vypadat.

Zdroje pro pochopení okrajů a os v D3…

  • Návod k našemu kodexu konvence marží – Mike Bostock
  • Průvodce generátory os v D3 — TutorialsTeacher
  • Referenční záznam D3 API o osách

Nyní, když vidíme, co náš graf ukazuje, chci to posunout na další úroveň přidáním aktualizace našich dat. K tomu použijeme metodu intervalu D3, abychom spustili nějaký kód nepřetržitě:

var flag = true;

// Run this code every second...
d3.interval(function(){
    // Flick between our two data arrays
    data = flag ? data0 : data1;

    // Update our chart with new data
    update(data);

    // Update our flag variable
    flag = !flag;
}, 1000)

Každých 1000 ms tato funkce spustí funkci aktualizace a změní data, která používáme mezi našimi dvěma různými poli.

Potřebujeme provést několik úprav v našem kódu, aby se vše aktualizovalo tak, jak chceme:

// Scales
var x = d3.scaleLinear()
    .range([0, width]);
var y = d3.scaleLinear()
    .range([height, 0]);

// Axes
var xAxisCall = d3.axisBottom(x)
var xAxis = g.append("g")
    .attr("class", "x-axis")
    .attr("transform", "translate(" + 0 + "," + height + ")");

var yAxisCall = d3.axisLeft(y)
var yAxis = g.append("g")
    .attr("class", "y-axis");

// Labels
xAxis.append("text")
    .attr("class", "axis-title")
    .attr("transform", "translate(" + width + ", 0)")
    .attr("y", -6)
    .text("Grade Point Average")
yAxis.append("text")
    .attr("class", "axis-title")
    .attr("transform", "rotate(-90)")
    .attr("y", 16)
    .text("Height / Centimeters");

var flag = true;

// Run this code every second...
d3.interval(function(){
    // Flick between our two data arrays
    data = flag ? data0 : data1;

    // Update our chart with new data
    update(data);

    // Update our flag variable
    flag = !flag;
}, 1000)

// Run for the first time
update(data0);

function update(data){
    // Update our scales
    x.domain([d3.min(data, function(d){ return d.gpa; }) / 1.05, 
        d3.max(data, function(d){ return d.gpa; }) * 1.05])
    y.domain([d3.min(data, function(d){ return d.height; }) / 1.05,
        d3.max(data, function(d){ return d.height; }) * 1.05])

    // Update our axes
    xAxis.call(xAxisCall);
    yAxis.call(yAxisCall);

    // Update our circles
    var circles = g.selectAll("circle")
        .data(data);

    circles.exit().remove()

    circles
        .attr("cx", function(d){ return x(d.gpa) })
        .attr("cy", function(d){ return y(d.height) })

    circles.enter()
        .append("circle")
            .attr("cx", function(d){ return x(d.gpa) })
            .attr("cy", function(d){ return y(d.height) })
            .attr("r", 5)
            .attr("fill", "grey");
}

Naše škálovací domény nastavujeme uvnitř naší aktualizační funkce, aby se přizpůsobily datům, se kterými pracujeme. Zde také voláme naše generátory os, které je odpovídajícím způsobem aktualizují. Máme pak matoucí blok kódu, který řeší, jak chceme, aby se naše kruhy aktualizovaly.

Zásadní koncept #5 — Vzor obecných aktualizací

Obecný vzor aktualizace se používá téměř v každé vizualizaci, kterou budete chtít vytvořit pomocí D3. Definuje chování prvků v našich datech, které by měly vstoupit, aktualizovat nebo opustit obrazovku. Jako začátečníkovi se může celý tento kód zdát trochu ohromující.

Podívejme se blíže na to, co každý z těchto řádků dělá.

Nejprve spojujeme naše nové pole dat s naším výběrem D3:

// JOIN new data with old elements.
var circles = g.selectAll("circle")
    .data(data);

Dále tento blok kódu odstraní všechny tečky, které již neexistují v našem novém poli dat:

// EXIT old elements not present in new data.
circles.exit().remove()

Zde aktualizujeme polohu všech bodů na obrazovce, které stále existují v našem novém datovém poli.

// UPDATE old elements present in new data.
circles
    .attr("cx", function(d){ return x(d.gpa) })
    .attr("cy", function(d){ return y(d.height) })

Nakonec přidáváme tečku pro každou položku v našem novém datovém poli, která nemá na obrazovce odpovídající kruh.

// ENTER new elements present in new data.
circles.enter().append("circle")
    .attr("cx", function(d){ return x(d.gpa) })
    .attr("cy", function(d){ return y(d.height) })
    .attr("r", 5)
    .attr("fill", "grey");

Záludná věc na pochopení obecného vzoru aktualizace je přesně zjistit, co selectAll(), enter() a exit() dělají. D3 funguje pomocí sady „virtuálních selektorů“, které můžeme použít ke sledování toho, které prvky je třeba aktualizovat.

I když vám stačí povrchové porozumění vzoru aktualizace s mnoha grafy, které byste chtěli vytvořit, celá knihovna bude mnohem jasnější, jakmile zjistíte, co každý z těchto selektorů dělá.

Zdroje pro pochopení obecného vzorce aktualizace v D3…

  • Návod k obecnému vzoru aktualizací – Quinton Louis Aiken
  • Interaktivní průzkum obecného vzorce aktualizací – Chris Given

Jakmile přidáme naše aktualizace, náš graf vypadá následovně:

Naše vizualizace se nyní pohybuje mezi dvěma poli dat, která chceme zobrazit. Přidám ještě jednu závěrečnou drobnost, aby náš graf vypadal trochu úhledněji.

Můžeme přidat některé krásné přechody pomocí skvělé sady přechodů D3. Nejprve definujeme přechodovou proměnnou v horní části naší aktualizační funkce, která rozloží každý z našich přechodů na dobu 750 ms.

// Standard transition for our visualization
var t = d3.transition().duration(750);

Všechny atributy, které nastavíme před voláním metody přechodu u výběru D3, budou nastaveny rovnou a všechny atributy, které nastavíme po této metodě přechodu, budou aplikovány postupně.

K našim osám můžeme přidat přechody takto:

// Update our axes
xAxis.transition(t).call(xAxisCall);
yAxis.transition(t).call(yAxisCall);

A do našich kruhů můžeme přidat přechody takto:

// Update our circles
var circles = g.selectAll("circle")
    .data(data);

circles.exit().transition(t)
    .attr("fill-opacity", 0.1)
    .attr("cy", y(0))
    .remove()

circles.transition(t)
    .attr("cx", function(d){ return x(d.gpa) })
    .attr("cy", function(d){ return y(d.height) })

circles.enter().append("circle")
    .attr("cx", function(d){ return x(d.gpa) })
    .attr("cy", y(0))
    .attr("r", 5)
    .attr("fill", "grey")
    .attr("fill-opacity", 0.1)
.transition(t)
    .attr("fill-opacity", 1)
    .attr("cy", function(d){ return y(d.height) });

Přecházíme mezi neprůhledností výplně 0 a 1, aby naše tečky jemně mizely a zanikaly, a plynule přesouváme aktualizační kruhy do jejich nových pozic.

Takže tady to máme. Nyní máme krásný bodový graf, který se aktualizuje mezi různými zdroji dat. Hotový produkt celého tohoto kódu najdete na mé stránce GitHub zde.

I když se zvládnutí pojmů v tomto článku může zdát jako obrovský krok, abyste mohli začít s D3, kód bude snáze a snáze pochopitelný praxí.

Brzy zjistíte, že stejné klíčové koncepty jsou základem každé vizualizace D3 a že jakmile budete vědět, jak jedna vizualizace v D3 funguje, můžete se rychle naučit postavit téměř cokoliv, co si dokážete představit.

Podívejte se na příklady na bl.ocks.org a blockbuilder.org a podívejte se na některé hotové implementace tolika zajímavých projektů. Stejně jako samotný D3 je veškerý tento kód open source, což znamená, že můžete jakýkoli kód zkopírovat na svůj místní počítač a použít jej pro své vlastní projekty.

Snadný způsob, jak začít s D3…

Pokud hledáte nejrychlejší a nejjednodušší způsob, jak se naučit D3, pak vedu kurz na Udemy, který nabízí komplexní úvod do knihovny. Kurz zahrnuje:

  • 7 hodin kvalitního videoobsahu.
  • Podrobný úvod k základním konceptům v D3, pokrývající všechna témata obsažená v tomto článku a další.
  • Čtyři úžasné třídní projekty k procvičení dovedností, které se učíte, na datech z reálného světa.
  • Velký důraz na návrh vizualizace dat, který vám pomáhá vytvářet vlastní vizualizace pro vaše vlastní data.
  • Návody 12 nejčastěji používaných vizualizací, které vás naučí, jak porozumět a přizpůsobit předem napsaný komunitní kód pro své vlastní účely.
  • Úvod do objektově orientovaného přístupu k vytváření složitých webových aplikací, kde se aktualizuje více vizualizací na stránce najednou.

Kurz můžete získat za zvýhodněnou cenu pouhých 20,99 $, když se zaregistrujete prostřednictvím tohoto odkazu zde.


No