[英]Animate position of svg rect on transition
Edit: here is an example Fiddle: https://jsfiddle.net/3c9dtLyh/6/ 编辑:这是一个小提琴的例子: https : //jsfiddle.net/3c9dtLyh/6/
I have a layout that I am attempting to animate to compare the arrangement on two different dates. 我正在尝试制作一个布局,以比较两个不同日期的安排。 What I would like to accomplish is a transition where items whose x,y position is different on the second date smoothly fly to the new position. 我想要完成的是一个过渡,其中第二个日期的x,y位置不同的项目将平稳地飞到新位置。 I have attempted to do this using an updateData function set to trigger onclick. 我尝试使用设置为触发onclick的updateData函数来执行此操作。
The layout looks like this: 布局如下所示:
I do not neccesarily expect this approach to work because how would the transition know which (x,y) pairs correspond to the correct item name in the new arrangement. 我并不一定希望这种方法行得通,因为过渡如何知道新安排中的(x,y)对与正确的商品名称相对应。 What am I missing about how these transitions work and how could I improve my approach? 我对这些过渡如何工作以及如何改进自己的方法遗漏了什么?
Here is the code I am using. 这是我正在使用的代码。 It's a relatively simple sequence of appending and svg element, drawing the rectangles, then (failing) to update their position on click. 这是一个相对简单的附加和svg元素序列,先绘制矩形,然后(失败)以更新其单击位置。
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
</style>
<body>
<div id = "chart">
</div>
<div id = "select_params">
<input name="updateButton"
type="button"
value="Update"
onclick="updateData()" />
</div>
</body>
<!-- load js libraries -->
<script src="https://d3js.org/d3.v4.min.js"></script> <!-- uses v4 of d3 -->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script> <!-- need to use this older version for tipsy -->
<script type="text/javascript" src="jquery.tipsy.js"></script> <!-- load from locally hosted source code -->
<!-- build the visualization -->
<script type='text/javascript'>
var item_width = 40, item_height = 60;
var margin = {top: 20, right: 50, bottom: 75, left: 40},
width = 700 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleLinear()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("#chart").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 + ")");
d3.csv("http://localhost:8080/udacity_test_vis_1/combined_item_pos.csv", function(data) {
// cast string to numeric
data.forEach(function(d) {
d.x_pos = +d.x_pos;
d.y_pos = +d.y_pos;
d.sales = +d.sales;
});
console.log(data);
var x_offset = 5, y_offset = 5;
x.domain(d3.extent(data, function(d) { return d.x_pos; })); // set the x domain
y.domain(d3.extent(data, function(d) { return d.y_pos; })); // set the y domain
svg.selectAll("g")
.data(data)
.enter()
.append("rect")
.filter(function(d){ return d.date == '1-20-2017'})
.attr("class", "dot")
.attr("width", item_width)
.attr("height", item_height)
.attr("x", function(d) { return x(d.x_pos) + x_offset; }) // x position of dots
.attr("y", function(d) { return y(d.y_pos) + y_offset; }) // y position of dots
.attr("rx", 5)
.attr("ry", 5)
.style("fill", "#1f5fc6") // color factor variable
.style("fill-opacity", 0.5);
svg.selectAll("g")
.data(data)
.enter()
.append("text")
.filter(function(d){ return d.date == '1-20-2017'})
.attr("x", function(d) { return x(d.x_pos) + item_width/2 + x_offset; })
.attr("y", function(d) { return y(d.y_pos) + item_height/2 + y_offset; })
.attr("font-size", 10)
.attr("text-anchor", "middle")
.attr("fill", "black")
.text(function(d){ return d.item_name});
});
function updateData() {
// grab the data again
d3.csv("http://localhost:8080/udacity_test_vis_1/combined_item_pos.csv", function(data) {
// cast string to numeric
data.forEach(function(d) {
d.x_pos = +d.x_pos;
d.y_pos = +d.y_pos;
d.sales = +d.sales;
});
var svg = d3.select("#chart").transition();
svg.selectAll("g")
.data(data)
.enter()
.append("rect")
.filter(function(d){ return d.date == '2-10-2017'})
.attr("class", "dot")
.attr("width", item_width)
.attr("height", item_height)
.attr("x", function(d) { return x(d.x_pos) + x_offset; }) // x position of dots
.attr("y", function(d) { return y(d.y_pos) + y_offset; }) // y position of dots
.attr("rx", 5)
.attr("ry", 5)
.style("fill", "#1f5fc6") // color factor variable
.style("fill-opacity", 0.5);
svg.selectAll("g")
.data(data)
.enter()
.append("text")
.filter(function(d){ return d.date == '2-10-2017'})
.attr("x", function(d) { return x(d.x_pos) + item_width/2 + x_offset; })
.attr("y", function(d) { return y(d.y_pos) + item_height/2 + y_offset; })
.attr("font-size", 10)
.attr("text-anchor", "middle")
.attr("fill", "black")
.text(function(d){ return d.item_name});
});
}
</script>
Here is my data: 这是我的数据:
,x_pos,y_pos,item_name,sales,date
0,1,1,S8221,2022,1-20-2017
1,2,1,NLC11,518,1-20-2017
2,3,1,35UUY,1614,1-20-2017
3,4,1,PPTNV,1059,1-20-2017
4,5,1,G0CWS,2183,1-20-2017
5,6,1,3JHUA,2513,1-20-2017
6,7,1,4HXGA,2251,1-20-2017
7,8,1,RYM9K,2330,1-20-2017
8,9,1,T8PUB,1476,1-20-2017
9,10,1,PLULW,1225,1-20-2017
10,1,2,YJ6S0,2403,1-20-2017
11,2,2,E9RGD,1361,1-20-2017
12,3,2,E2SW4,1131,1-20-2017
13,4,2,BZPGX,698,1-20-2017
14,5,2,0K682,1855,1-20-2017
15,6,2,D8UZW,2371,1-20-2017
16,7,2,USKY7,1851,1-20-2017
17,8,2,D0L0Y,1767,1-20-2017
18,9,2,P1AGP,1025,1-20-2017
19,10,2,9LT7O,1380,1-20-2017
20,1,3,1J184,1108,1-20-2017
21,2,3,RJDEG,2106,1-20-2017
22,3,3,LTSLR,1980,1-20-2017
23,4,3,ET3DF,2700,1-20-2017
24,5,3,42W1W,2194,1-20-2017
25,6,3,5QTJN,958,1-20-2017
26,7,3,O8XKY,2381,1-20-2017
27,8,3,LS9NW,516,1-20-2017
28,9,3,0MPZ7,2198,1-20-2017
29,10,3,R4E3J,2494,1-20-2017
30,1,4,WFPPY,2349,1-20-2017
31,2,4,MT2DB,2525,1-20-2017
32,3,4,6DRYS,600,1-20-2017
33,4,4,NVV0S,1556,1-20-2017
34,5,4,ODGZ2,912,1-20-2017
35,6,4,E3NLS,931,1-20-2017
36,7,4,9FFZ7,722,1-20-2017
37,8,4,UKZGF,2170,1-20-2017
38,9,4,XXORI,896,1-20-2017
39,10,4,QYU9Q,1104,1-20-2017
40,1,5,4KQPU,1562,1-20-2017
41,2,5,S3AYK,2298,1-20-2017
42,3,5,5W3CE,2580,1-20-2017
43,4,5,T0S7H,1677,1-20-2017
44,5,5,02SJG,1972,1-20-2017
45,6,5,GBMNZ,1845,1-20-2017
46,7,5,2Y7KH,982,1-20-2017
47,8,5,3WMOL,1952,1-20-2017
48,9,5,93KLU,2240,1-20-2017
49,10,5,K80OQ,2467,1-20-2017
50,1,6,2SIJS,1788,1-20-2017
51,2,6,5ZJ7V,2277,1-20-2017
52,3,6,HTL99,873,1-20-2017
53,4,6,C06QP,2185,1-20-2017
54,5,6,2S1YI,580,1-20-2017
55,6,6,IQ0L8,2395,1-20-2017
56,7,6,PEE2Y,2299,1-20-2017
57,8,6,6DEWK,2019,1-20-2017
58,9,6,9FY5B,1517,1-20-2017
59,10,6,NZQ54,2624,1-20-2017
60,1,7,C4SVV,1823,1-20-2017
61,2,7,Q4C4I,2339,1-20-2017
62,3,7,996OQ,1621,1-20-2017
63,4,7,PISK6,895,1-20-2017
64,5,7,KOKHE,1315,1-20-2017
65,6,7,6P4FT,1467,1-20-2017
66,7,7,3FY75,2085,1-20-2017
67,8,7,9YCNB,992,1-20-2017
68,9,7,NXXK1,2080,1-20-2017
69,10,7,4RDHV,2031,1-20-2017
0,6,1,9FFZ7,592,2-10-2017
1,1,6,E2SW4,622,2-10-2017
2,6,7,PLULW,1699,2-10-2017
3,8,3,ET3DF,784,2-10-2017
4,9,4,KOKHE,1092,2-10-2017
5,2,6,5ZJ7V,1691,2-10-2017
6,4,5,9FY5B,630,2-10-2017
7,9,4,G0CWS,1523,2-10-2017
8,9,2,PISK6,1778,2-10-2017
9,6,4,35UUY,2107,2-10-2017
10,3,5,5QTJN,1751,2-10-2017
11,6,6,NLC11,526,2-10-2017
12,8,2,C06QP,2308,2-10-2017
13,8,3,XXORI,1453,2-10-2017
14,5,1,E9RGD,1864,2-10-2017
15,7,2,HTL99,1222,2-10-2017
16,3,3,PEE2Y,2050,2-10-2017
17,9,7,GBMNZ,1941,2-10-2017
18,3,1,T8PUB,1440,2-10-2017
19,5,1,3WMOL,2692,2-10-2017
20,7,7,S3AYK,523,2-10-2017
21,1,5,BZPGX,2245,2-10-2017
22,2,1,S8221,2241,2-10-2017
23,9,7,IQ0L8,566,2-10-2017
24,8,5,D8UZW,1769,2-10-2017
25,3,1,RYM9K,1044,2-10-2017
26,4,6,4HXGA,2650,2-10-2017
27,2,2,WFPPY,2203,2-10-2017
28,2,4,93KLU,2289,2-10-2017
29,7,3,P1AGP,1084,2-10-2017
30,4,3,3JHUA,1364,2-10-2017
31,1,4,9LT7O,1198,2-10-2017
32,4,6,4RDHV,771,2-10-2017
33,10,7,T0S7H,873,2-10-2017
34,3,6,NXXK1,2391,2-10-2017
35,8,2,2SIJS,811,2-10-2017
36,8,4,LTSLR,1670,2-10-2017
37,6,7,02SJG,1880,2-10-2017
38,9,3,0MPZ7,2090,2-10-2017
39,2,6,E3NLS,2350,2-10-2017
40,7,6,QYU9Q,1092,2-10-2017
41,6,3,0K682,894,2-10-2017
42,1,5,LS9NW,1928,2-10-2017
43,7,7,NVV0S,951,2-10-2017
44,9,4,996OQ,670,2-10-2017
45,7,6,USKY7,706,2-10-2017
46,10,4,Q4C4I,2270,2-10-2017
47,4,2,UKZGF,1691,2-10-2017
48,10,3,RJDEG,597,2-10-2017
49,10,2,1J184,1921,2-10-2017
50,2,3,5W3CE,2604,2-10-2017
51,5,5,3FY75,1260,2-10-2017
52,1,1,6DEWK,2491,2-10-2017
53,7,5,9YCNB,1743,2-10-2017
54,4,7,6DRYS,2450,2-10-2017
55,5,2,MT2DB,1292,2-10-2017
56,8,5,C4SVV,1395,2-10-2017
57,3,7,ODGZ2,2685,2-10-2017
58,10,4,2S1YI,2617,2-10-2017
59,1,2,YJ6S0,1611,2-10-2017
60,6,3,2Y7KH,2188,2-10-2017
61,5,4,4KQPU,1413,2-10-2017
62,10,1,D0L0Y,2291,2-10-2017
63,5,1,NZQ54,1405,2-10-2017
64,5,2,6P4FT,1885,2-10-2017
65,3,1,PPTNV,1442,2-10-2017
66,1,5,K80OQ,2140,2-10-2017
67,4,5,42W1W,1697,2-10-2017
68,2,7,O8XKY,1007,2-10-2017
69,10,6,R4E3J,887,2-10-2017
So, I took a few minutes to completely refactor your code into proper d3
style. 因此,我花了几分钟时间将代码完全重构为正确的d3
样式。 This aims to demonstrate a couple things: 目的是演示几件事情:
enter
, update
, exit
pattern. 正确使用enter
, update
, exit
模式。 g
to group elements and position them together. 使用g
分组元素并将它们放置在一起的正确方法。 Here is the code running . 这是运行代码 。
Commented code: 注释代码:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
</style>
<body>
<div id="chart">
</div>
<div id="select_params">
<input name="updateButton" type="button" value="Update" onclick="updateData()" />
</div>
</body>
<!-- load js libraries -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- uses v4 of d3 -->
<!-- build the visualization -->
<script type='text/javascript'>
var item_width = 40,
item_height = 60;
var margin = {
top: 20,
right: 50,
bottom: 75,
left: 40
},
width = 700 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleLinear()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("#chart").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 + ")");
// a single function to draw
function draw(data, someDate) {
data.forEach(function(d) {
d.x_pos = +d.x_pos;
d.y_pos = +d.y_pos;
d.sales = +d.sales;
});
// pre-filter data
data = data.filter(function(d) {
return d.date === someDate
});
var x_offset = 5,
y_offset = 5;
x.domain(d3.extent(data, function(d) {
return d.x_pos;
})); // set the x domain
y.domain(d3.extent(data, function(d) {
return d.y_pos;
})); // set the y domain
// create an update selection with a key function
var g_sel = svg.selectAll("g")
.data(data, function(d) {
return d.item_name;
});
// get rid of those leaving the update
g_sel.exit().remove();
// our entering g
var g_ent = g_sel.enter()
.append("g");
// add our rects to our g
g_ent.append("rect")
.attr("class", "dot")
.attr("width", item_width)
.attr("height", item_height)
.attr("rx", 5)
.attr("ry", 5)
.style("fill", "#1f5fc6") // color factor variable
.style("fill-opacity", 0.5);
// add our text to our g
g_ent.append("text")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("dx", item_width / 2)
.attr("dy", item_height / 2)
.text(function(d) {
return d.item_name
});
// UPDATE + ENTER selection
g_sel = g_ent.merge(g_sel);
// move them into position with transition
g_sel
.transition()
.attr("transform", function(d) {
return "translate(" + (x(d.x_pos) + x_offset) + "," + (y(d.y_pos) + y_offset) + ")";
});
}
d3.csv("test.csv", function(data) {
draw(data, '1-20-2017');
});
function updateData() {
d3.csv("test.csv", function(data) {
draw(data, '2-10-2017');
});
}
</script>
Heres my attempt: https://jsfiddle.net/guanzo/3c9dtLyh/10/ 这是我的尝试: https : //jsfiddle.net/guanzo/3c9dtLyh/10/
There are multiple data points that share the same position, which is why some rectangles are overlapping. 有多个共享相同位置的数据点,这就是为什么某些矩形重叠的原因。 I made a lot of changes to your code that resulted in less repetition. 我对您的代码进行了很多更改,从而减少了重复。
Your data contains duplicate item_names
with different dates/positions, but in your visualization you seem to want to only show items at a single date. 您的数据包含重复的item_names
,它们具有不同的日期/位置,但是在您的可视化中,您似乎只想显示单个日期的项目。 Therefore, you only need to pass d3 data for a certain date, versus passing d3 ALL the data and then filtering. 因此,您只需要传递特定日期的d3数据,而不是传递d3所有数据然后进行过滤。
Your code: 您的代码:
svg.selectAll("g")
.data(data)
.enter()
.append("rect")
.filter(function(d){ return d.date == '1-20-2017'})
My code: 我的代码:
var firstDateData = data.filter(d=>d.date == '1-20-2017');
var groups = svg.selectAll("g")
.data(firstDateData, d=> d.item_name)
The difference between these 2 is that in my example, D3 is only aware of a single set of item_names
on date 1-20-2017
. 这两个之间的区别在于,在我的示例中,D3仅知道日期为1-20-2017
的一组item_names
。 Therefore when i update the date with item_names
on date 2-10-2017
, D3 will automatically move all updated rectangles to their new position. 因此,当我使用item_names
更新日期为2-10-2017
日期2-10-2017
,D3将自动将所有更新的矩形移动到其新位置。 How? 怎么样?
Here is where your question comes into play: 这是您的问题发挥作用的地方:
I do not neccesarily expect this approach to work because how would the transition know which (x,y) pairs correspond to the correct item name in the new arrangement 我并不一定希望这种方法行得通,因为过渡如何知道新安排中的(x,y)对对应于正确的商品名称
This is because i associated each rectangle with an item_name
. 这是因为我将每个矩形与item_name
关联。 D3s data
function can take an optional 2nd parameter that specifies HOW the data is bound to the rectangles. D3s data
函数可以采用可选的第二个参数,该参数指定如何将数据绑定到矩形。 This is called the key function. 这称为按键功能。
svg.selectAll("g").data(firstDateData, d=> d.item_name)
In this case, i told d3 that each group (rectangles and their text) is bound to item_name
. 在这种情况下,我告诉d3,每个组(矩形及其文本)都绑定到item_name
。 Therefore, the next time i pass data to D3, it tries to match existing elements (that are associated with an item_name
) to the data (which contains item_names
). 因此,下一次我将数据传递给D3时,它将尝试将现有元素(与item_name
关联)与数据(包含item_names
)进行匹配。 If in my new data, i pass an item_name
that corresponds to an existing element, and the data contains a new x and y position, D3 will move to element to that new position. 如果在我的新数据中,我传递了与现有元素相对应的item_name
,并且数据包含新的x和y位置,则D3将移动到该元素到该新位置。
Note that even though i'm talking about rectangles, i bound data to the g
element, which contains the rectangle and the text. 请注意,即使我在谈论矩形,我也会将数据绑定到g
元素,该元素包含矩形和文本。
Feel free to ask any questions, i made a lot of changes that i didn't discuss. 随时问任何问题,我做了很多我没有讨论过的更改。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.