#javascript #d3.js
#javascript #d3.js
Вопрос:
Я хочу нарисовать несколько строк на линейном графике d3 и преобразовать каждую строку анимацией. Вот рабочий пример, но с отображением 1 строки за раз.
https://www.d3-graph-gallery.com/graph/line_change_data.html
Я пытаюсь расширить его, чтобы показывать несколько строк одновременно. А затем при нажатии кнопки Dataset 2 отображается новый набор строк и выполняется переход к каждой из них. Моя попытка здесь
// Create 2 datasets
var data1 = [
[{
ser1: 0.3,
ser2: 4
},
{
ser1: 2,
ser2: 16
},
{
ser1: 3,
ser2: 8
}
],
[{
ser1: 1,
ser2: 7
},
{
ser1: 4,
ser2: 1
},
{
ser1: 6,
ser2: 8
}
]
];
var data2 = [
[{
ser1: 4,
ser2: 7
},
{
ser1: 2,
ser2: 5
},
{
ser1: 2,
ser2: 3
}
],
[{
ser1: 1,
ser2: 10
},
{
ser1: 5,
ser2: 8
},
{
ser1: 7,
ser2: 3
}
]
];
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 50
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform",
"translate(" margin.left "," margin.top ")");
// Initialise a X axis:
var x = d3.scaleLinear().range([0, width]);
var xAxis = d3.axisBottom().scale(x);
svg.append("g")
.attr("transform", "translate(0," height ")")
.attr("class", "myXaxis")
// Initialize an Y axis
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y);
svg.append("g")
.attr("class", "myYaxis")
// Create a function that takes a dataset as input and update the plot:
function update(data) {
// Create the X axis:
x.domain([0, d3.max(data, function(d) {
return d3.max(d, function(d) {
return d.ser1;
});
})]);
svg.selectAll(".myXaxis").transition()
.duration(3000)
.call(xAxis);
// create the Y axis
y.domain([0, d3.max(data, function(d) {
return d3.max(d, function(d) {
return d.ser2;
});
})]);
svg.selectAll(".myYaxis")
.transition()
.duration(3000)
.call(yAxis);
data.forEach(function(data, i) {
// Create a update selection: bind to the new data
var u = svg.selectAll(`.lineTest${i}`)
.data([data], function(d) {
return d.ser1
});
// Update the line
u
.enter()
.append("path")
.attr("class", `.lineTest${i}`)
.merge(u)
.transition()
.duration(3000)
.attr("d", d3.line()
.x(function(d) {
return x(d.ser1);
})
.y(function(d) {
return y(d.ser2);
}))
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2.5);
});
}
// At the beginning, I run the update function on the first dataset:
update(data1);
<script src="https://d3js.org/d3.v5.min.js"></script>
<!-- Add 2 buttons -->
<button onclick="update(data1)">Dataset 1</button>
<button onclick="update(data2)">Dataset 2</button>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
Но проблема в том, что когда я нажимаю кнопку Dataset 2, она рисует новые строки, старые строки все еще отображаются, и между ними нет перехода.
Кто-нибудь знает, что здесь не так?
Спасибо
Комментарии:
1. Я думаю, что это работает так, как вы хотите здесь: jsfiddle.net/kqjcobah . Однако есть пара вещей: объединения данных позаботятся о рекурсии, поэтому нет необходимости
forEach
. Когда вы устанавливаете класс, не ставьте точку — ставьте точку только при выборе класса. Я также перенес функцию line в переменную. Надеюсь, это поможет!
Ответ №1:
Пара замечаний:
- Как отметил @Ryan Morton, просто передайте весь набор строк в d3, он позаботится обо всем остальном. Нет необходимости в
forEach
; - Вы использовали
attr('class', '.lineTest')
, но это дало ему класс, который начинался с точки. Это сделало невозможным выбор с помощьюd3.select('.lineTest')
. Удаление точки исправило это; - Поскольку ваша ключевая функция выбирает
d.ser1
, ноd
представляет собой массив объектов,ser1
не определено. Я обновил функцию ключа, чтобы вместо этого использовать индекс.
// Create 2 datasets
var data1 = [
[{
ser1: 0.3,
ser2: 4
},
{
ser1: 2,
ser2: 16
},
{
ser1: 3,
ser2: 8
}
],
[{
ser1: 1,
ser2: 7
},
{
ser1: 4,
ser2: 1
},
{
ser1: 6,
ser2: 8
}
]
];
var data2 = [
[{
ser1: 4,
ser2: 7
},
{
ser1: 2,
ser2: 5
},
{
ser1: 2,
ser2: 3
}
],
[{
ser1: 1,
ser2: 10
},
{
ser1: 5,
ser2: 8
},
{
ser1: 7,
ser2: 3
}
]
];
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 50
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform",
"translate(" margin.left "," margin.top ")");
// Initialise a X axis:
var x = d3.scaleLinear().range([0, width]);
var xAxis = d3.axisBottom().scale(x);
svg.append("g")
.attr("transform", "translate(0," height ")")
.attr("class", "myXaxis")
// Initialize an Y axis
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y);
svg.append("g")
.attr("class", "myYaxis")
// Create a function that takes a dataset as input and update the plot:
function update(data) {
// Create the X axis:
x.domain([0, d3.max(data, function(d) {
return d3.max(d, function(d) {
return d.ser1;
});
})]);
svg.selectAll(".myXaxis").transition()
.duration(3000)
.call(xAxis);
// create the Y axis
y.domain([0, d3.max(data, function(d) {
return d3.max(d, function(d) {
return d.ser2;
});
})]);
svg.selectAll(".myYaxis")
.transition()
.duration(3000)
.call(yAxis);
// Create a update selection: bind to the new data
var lines = svg.selectAll(`.lineTest`)
.data(data, function(d, i) {
return i;
});
lines.exit()
.remove();
// Update the line
lines.enter()
.append("path")
.attr("class", "lineTest")
.merge(lines)
.transition()
.duration(3000)
.attr("d", d3.line()
.x(function(d) {
return x(d.ser1);
})
.y(function(d) {
return y(d.ser2);
}))
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2.5);
}
// At the beginning, I run the update function on the first dataset:
update(data1);
<script src="https://d3js.org/d3.v5.min.js"></script>
<!-- Add 2 buttons -->
<button onclick="update(data1)">Dataset 1</button>
<button onclick="update(data2)">Dataset 2</button>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>