2016-11-04 2 views
2

Я сделал простую гистограмму. Я хочу обновить бары («на лету»), переключая разные подмножества моих данных. Из того, что я видел, существует, по-видимому, два подхода:Обновление гистограммы с .update, .enter и .exit?

  1. Нарисуйте диаграмму и нажмите кнопку (или что-то еще), чтобы удалить весь график и перерисовать его.
  2. Используйте разные данные для «обновления», «введите» и «выйдите» из графика с помощью кнопки.

Я пытаюсь вариант 2. Вот мой код:

// set dimensions 
var margin = {top: 10, right: 20, bottom: 50, left: 100}; 
var height = 600 -margin.top - margin.bottom; 
var width = 600 - margin.left - margin.right; 

// set ranges 
var x = d3.scale.linear().range([width, 0]); 
var y = d3.scale.ordinal().rangeRoundBands([0, height], .05); 

// define y axis 
var yAxis = d3.svg.axis() 
.scale(y) 
.orient("left"); 

// add svg container 
var svg = d3.select("#chart") 
.append("svg") 
.attr("height", height + margin.top + margin.bottom) 
.attr("width", width + margin.left + margin.right) 
.append("g") 
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

// load data 
d3.json("data.json", function(data) { 

    // format data 
    data.forEach(function(d) { 
    d.enrols = +d.enrols; 
    }); 

    // register click event 
    d3.selectAll(".toggle").on("click", function() { 
    render(this.id); 
    }); 

    // initialise 
    render("group-1"); 

    function render(selection) { 

    // filter data 
    var subset = data.filter(function(d) { return d.group === selection}); 

    // scale range of data 
    x.domain([0, d3.max(subset, function(d) { return d.enrols; })]); 
    y.domain(subset.map(function(d) { return d.month; })); 

    // update selection 
    var bars = svg.selectAll(".bars") 
    .data(subset, function(d) { return d.month; }); 

    // add bars 
    bars.enter() 
    .append("g") 
    .attr("class", "bar") 
    .attr("transform", function (d, i) { 
     return "translate(0," + y(d.month) + ")"; 
    }); 

    bars.append("rect") 
    .attr("height", y.rangeBand()) 
    .attr("width", function (d) { 
     return width - x(d.enrols); 
    }); 

    bars.append("text") 
    .attr("x", function (d) { 
     return width - x(d.enrols) - 15; 
    }) 
    .attr("y", y.rangeBand()/2) 
    .attr("dy", ".35em") 
    .text(function (d) { 
     return d.enrols; 
    }); 

    bars.exit().remove(); 

    // add y axis 
    svg.append("g") 
    .attr("class", "y axis") 
    .call(yAxis); 

    } 

}); 

Проблема мои старые данные никогда не получает вышли, новые бары добавляются, но старые остаются. Я читал теорию выбора d3, но я нахожу их трудно реализовывать на практике.

Адрес codepen. Вот data.json.

+0

'SelectAll («») бары' неправильно, поскольку нет никаких элементов с классом 'bars', есть только элементы с классом' bar'. Вы создаете несколько осей. И вы также добавляете несколько баров к одному и тому же элементу группы 'g'. Посмотрите на SVG, сгенерированный в кодексе. – Ashitaka

+0

Вы имеете в виду вот так: http://codepen.io/24ma13wg/pen/jVOgRj? Кажется, не помогает? – 24ma13wg

+0

Очень хорошо! Вы решили почти все проблемы в кодеде. Вы все равно добавляете новую ось в свою диаграмму каждый раз, когда вызывается функция «render». Вы должны это исправить. В любом случае, теперь я могу объяснить вам, что данные d3 объединяются. – Ashitaka

ответ

0

Ваша гистограмма требует очень простой модификации. Вот недостающая часть головоломки:

Когда мы используем функцию data(), D3 выполняет join operation между существующими элементами DOM и вашими значениями данных. Вы сохраняете результат этой операции по переменной bars. Мы можем назвать несколько методов на bars.

Метод exit() возвращает элементы, которые будут удалены. Если у нас есть 3 бара в DOM, и мы вызываем функцию data() с массивом с двумя значениями данных, exit() вернет панель, которая будет удалена.

Аналогично, метод enter() возвращает элементы, которые будут добавлены. Если у нас есть 2 бара на DOM, и мы вызываем функцию data() с массивом с 3 значениями данных, то enter() вернет новый бар, который будет добавлен.

Тогда как насчет баров, которые собираются обновить? Ну, это то, что хранится в переменной bars!

Переводя это код очень прост:

Это:

bars.enter() 
    .append("g") 
    ... 

превращается в это:

var newBars = bars.enter() 
    .append("g") 
    ... 

И тогда появляется каждый раз, когда это:

bars.append(...) 

превращается в это :

newBars.append(...) 

Каждый раз, когда есть новый бар, мы append что-то в DOM.

Каждый раз, когда нам нужно обновлять панель, мы получаем select что-то из DOM.

bars обратитесь к барам, которые будут обновлены. Таким образом, мы добавим этот код, чтобы обновить существующие бары:

bars.select('rect') 
    .attr("width", function (d) { 
    return width - x(d.enrols); 
    }); 

bars.select('text') 
    .attr("x", function (d) { 
    return width - x(d.enrols) - 15; 
    }) 
    .text(function (d) { 
    return d.enrols; 
    }); 
+0

Я следую вашей логике: 'bars' содержит объединенные элементы и данные dom; сохраните новые бары, которые нужно добавить в переменную, и нет необходимости удалять что-либо, но обновляйте с помощью 'selectAll'. Все, что я получаю, это ширина моих баров, увеличивающая бит на каждом переключении? Обновлен codepen: http://codepen.io/24ma13wg/pen/jVOgRj – 24ma13wg

+0

Извините, ошибся. Там, где я написал 'selectAll', он должен читать' select'. Я обновил свой ответ. – Ashitaka

+0

Кстати, между объединениями данных в D3 v3 и D3 v4 существует несколько различий. Если вы заинтересованы в понимании этих различий, посмотрите, что [делает хорошее программное обеспечение хорошим] (https://medium.com/@mbostock/what-makes-software-good-943557f8a488#708a). – Ashitaka

3

Очень хорошо, что вы выбрали второй вариант по сравнению с первым: удаление всех элементов и рисование диаграммы снова - очень ленивый способ кодирования.

Однако, в вашем случае, поскольку длина данных не изменяется, вы не нуждаетесь в и не нуждаетесь в вариантах «введите», «обновить» и «выйти»!

Это все намного легче, если вы перемещаете некоторые не изменяя части за пределами render функции:

y.domain(data.map(function(d) { 
    return d.month; 
})); 

var bars = svg.selectAll(".bars") 
    .data(data, function(d) { 
     return d.month; 
    }).enter() 
    .append("g") 
    .attr("class", "bar") 
    .attr("transform", function(d, i) { 
     return "translate(0," + y(d.month) + ")"; 
    }); 

var rects = bars.append("rect") 
    .attr("height", y.rangeBand()) 
    .attr("width", function(d) { 
     return width - x(d.enrols); 
    }); 

var texts = bars.append("text") 
    .attr("x", function(d) { 
     return width - x(d.enrols) - 15; 
    }) 
    .attr("y", y.rangeBand()/2) 
    .attr("dy", ".35em") 
    .text(function(d) { 
     return d.enrols; 
    }); 

svg.append("g") 
    .attr("class", "y axis") 
    .call(yAxis); 

И держать в нем только то, что действительно меняется:

  • домен Вашего х масштаба,
  • Данные, связанные с группами,
  • Переходы для прямоугольников и текстов.

Было бы путь меньше:

function render(selection) { 

    // filter data 
    var subset = data.filter(function(d) { 
     return d.group === selection 
    }); 

    // scale range of data 
    x.domain([0, d3.max(subset, function(d) { 
     return d.enrols; 
    })]); 

    bars.data(subset, function(d) { 
     return d.month; 
    }); 

    rects.transition().duration(500).attr("width", function(d) { 
     return width - x(this.parentNode.__data__.enrols); 
    }); 

    texts.transition().duration(500).attr("x", function(d) { 
      return width - x(this.parentNode.__data__.enrols) - 15; 
     }) 
     .text(function(d) { 
      return this.parentNode.__data__.enrols; 
     }); 

} 

Вот ваш CodePen: http://codepen.io/anon/pen/qqBzWQ?editors=0010

PS: Это было бы еще проще, если избавиться от этих групп, добавление прямоугольники и тексты непосредственно.

+0

'this.parentNode .__ data __. Enrols'? не 'd.enrols'? – 24ma13wg

+0

Нет, не 'd', если мы не создадим здесь« ввод »,« обновление »и« выход », чего я избегаю. –

+0

Спасибо. Это упражнение для меня - так что, если длина данных изменилась, т. Е. У меня не было данных за ноябрь и декабрь во втором подмножестве данных? – 24ma13wg

Смежные вопросы