Иън Джонсън, @enjalot, създава Building Blocks — просто уеб приложение за редактиране на примерни кодове, които са съвместими с «http://bl.ocks.org, което е де факто средата за споделяне на d3. js примери за код. Неговата кампания в kickstarter за проекта вече е финансирана два пъти и остават 22 дни до края. В момента е лесно да отидете на bl.ocks.org и бързо да се вдъхновите от различни проекти. Въпреки това е по-трудно да се играе директно с кода. Иън Джонсън ще промени това.
Building Blocks ще позволи на много потребители, които преди това не са били запознати с D3, лесно да си изцапат ръцете с кода. По същия начин ще позволи на по-опитни D3 разработчици да настройват настройките, за да получат по-задълбочено разбиране на това, което всъщност се случва.
За да отпразнувам, ще обсъдя „Общия шаблон за актуализиране“ в D3. Това е въвеждащ модел, който е ключов за разбирането как данните са обвързани с DOM в D3.
Кодът, който ще обсъждаме, се намира на адрес http://bl.ocks.org/mbostock/3808218. Да започваме.
var alphabet = "abcdefghijklmnopqrstuvwxyz".split(""); var width = 960; var height = 500;
var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(32," + (height / 2) + ")");
Добавяме елемент svg
с определена ширина и височина към тялото. След това добавяме елемент g
към това svg
. Елемент g
се използва като контейнер за групиране на други обекти. Ние също превеждаме този g
елемент 32px надясно и 250px надолу, за да дадем някаква подложка и повече или по-малко вертикално да го центрираме.
// The initial display. update(alphabet);
setInterval(function() { update( d3 .shuffle(alphabet) .slice(0, Math.floor(Math.random() * 26)) .sort() ); }, 1500);
Първоначално извикваме функцията за актуализиране с цялата азбука. След това на всеки 1,5 секунди извикваме функцията с произволна извадка от букви от азбуката по азбучен ред. Свързването на данни се извършва във функцията за актуализиране.
function update (data) { // DATA JOIN var text = svg .selectAll("text") .data(data); // "UPDATE SELECTION" text.attr("class", "update"); // "ENTER SELECTION" text .enter() .append("text") .attr("class", "enter") .attr("x", function(d, i) { return i * 32; }) .attr("dy", ".35em"); // "ENTER + UPDATE SELECTION" // I.E., THE NEW UPDATE SELECTION text.text(function(d) { return d; }); // "EXIT SELECTION" text.exit().remove(); }
Обединяването на данни е последвано от операции върху селекциите „влизане, актуализиране и излизане“. Нека разбием всяка стъпка.
// DATA JOIN
var text = svg
.selectAll("text")
.data(data);
Това ще избере всички text
елементи в рамките на svg
и ще прикачи данни към тях. По подразбиране данните се прикачват по индекс, така че данните в data[0] ще бъдат прикачени към първия text
елемент, докато данните в data[3] ще бъдат прикачени към четвъртия text
елемент, например. Най-объркващото в този модел е, че тези текстови елементи може да съществуват или да не съществуват все още.
В този пример променливата text
ще върне обект с enter
и exit
методи. Той също така ще има свойство, което сочи към масива от избрани text
елементи. Ако елементите text
не съществуват, този масив ще бъде празен. Въпреки това, той все още ще бъде със същата дължина като масива от данни, който сме предали, и предадените данни все още са свързани с всеки индекс.
Ако всички text
елементи съществуват, елементите ще получат нови данни, отново съответстващи на техните индекси. Ако някои text
елементи съществуват, но получихме нов масив от данни, чиято дължина е по-дълга от броя text
елементи, които имаме в момента в DOM, тогава съществуващите text
елементи ще получат нови данни и нашата променлива text
също ще следи новите допълнителни индекси/данни за данните, които са получени, но все още няма съответни елементи за съхраняване на тези данни.
// UPDATE SELECTION
text.attr("class", "update");
След обединяването на данните, нашата променлива text
сама по себе си е нашата „селекция за актуализиране“. Той съответства на вече съществуващи елементи в DOM, които са получили нови данни. При първоначалното изпълнение на нашата функция за актуализиране това няма да направи нищо, тъй като все още няма съществуващи text
елементи.
// "ENTER SELECTION" text .enter() .append("text") .attr("class", "enter") .attr("x", function(d, i) { return i * 32; }) .attr("dy", ".35em");
Това е нашата „селекция за въвеждане“. Той съответства на данните, които са получени, но няма съответни елементи за съхраняване на тези данни. Можете да мислите за това като за „за всяка част от данните в променлива text
, която все още няма съществуващ елемент, направете нещата след извикването enter().
При нашето първоначално извикване на функцията за актуализиране, това ще се изпълнява за всяка част от вмъкнати данни. Добавяме нов елемент text
към нашия елемент g
, даваме му клас „enter“ и задаваме атрибутите x и y за всяка част от данните.
// "ENTER + UPDATE SELECTION" // I.E., THE NEW UPDATE SELECTION text.text(function(d) { return d; });
Отново, нашата променлива text
съответства на нашата „селекция за актуализиране“. След като извършихме нашите операции „въвеждане“, променливата се актуализира, за да включи всички нововмъкнати text
елементи. Това е така, защото нашата „селекция за актуализиране“ просто съответства на съществуващи елементи в DOM, които са получили нови данни.
// "EXIT SELECTION"
text.exit().remove();
Това е нашият „изходен избор“. Това съответства на всички съществуващи DOM елементи в нашата селекция, за които не са намерени нови данни. Обичайно е да искате да „премахнете“ елементите „изход от избора“ от DOM. При първоначалното ни преминаване тази селекция ще бъде празна.
За да обединим всичко, кажете, че имаме 5 text
елемента в нашия svg
с данни съответно „a“, „b“, „c“, „d“, „e“. След това извикваме нашата функция за актуализиране с друг кръг от данни – „j“, „k“, „l“.
Нашето „обединяване на данни“ ще съедини „j“ с нашия първи text
елемент, „k“ с нашия втори и „l“ с нашия трети. „d“ все още ще бъде свързано с четвъртия елемент, а „e“ ще бъде свързано с последния елемент.
Нашата „селекция за актуализиране“ ще съответства само на елементите, които са получили нови данни – първите три елемента.
Няма да имаме „въвеждане на селекция“, защото имаме елементи за всички наши нови части от данни. Не е необходимо да добавяме нови елементи, за да представим нашите данни.
Нашият „изход“ ще съответства на нашите последни два елемента – елементите, свързани с „d“ и „e“. Не бяха намерени нови данни за тези елементи, така че ги премахваме.
В следващата ни итерация, да кажем, че извикваме нашата функция за актуализиране с масив от стойности — „f“, „i“, „m“, „p“, „q“.
Ние отново свързваме новите си данни със съществуващите text
елементи. Имаме обаче 5 части от данни и в момента само 3 text
елемента. Тези 3 елемента ще съответстват съответно на „f“, „i“ и „m“. Вижте http://bl.ocks.org/mbostock/3808221 за пример как да добавите ключ към „обединяването на данни“, за да замените свързването „по индекс“ по подразбиране.
Нашата „селекция за актуализиране“ отново съответства само на съществуващите елементи, които са получили нови данни – първите три елемента.
Нашата „селекция за въвеждане“ съответства на части от данни „p“ и „q“ — данните, които нямат текущи елементи, които да ги поместят. Така добавяме нов text
елемент за всеки от тях.
Нашата „изходна селекция“ ще бъде празна, защото има нови данни за всичките ни съществуващи елементи.
Твърдото разбиране на „общия шаблон за актуализация“ е ключово в D3 и мнозина намират модела за много неинтуитивен в началото, така че се надявам тази статия да изясни известно объркване! Чувствайте се свободни да оставите коментар или да се свържете с мен в twitter @QuintonAiken, ако имате въпроси.
Първоначално публикувано в quintonlouisaiken.com на 27 юли 2015 г.