我在d3中整理了一个简单的自定义仪表板,其中包含了我从各种来源在线改编的示例。虽然我已经取得了稳步进展,但我仍然遇到了一些可能或可能没有相关的问题,可能与d3过渡的错误应用有关。
我想解决的问题如下: 1.饼图在加载时不会显示任何内容。只有在更改日期字段时才会加载图表。 2.饼图图例未正确更新,从示例1切换到示例2在图例中留下一个空框,即使该示例不包含该类的实例。 3.标签在条形图上没有正确更新,另外条形图的顺序也没有正确更新。这些可能都与过渡问题有关。
这是演示行为的小提琴:
https://jsfiddle.net/Sledge/xo1s9dz9/6/
这是我的代码:
var item_width = 40, item_height = 50;
var margin = {top: 50, 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([0, height]);
var colorScale = d3.scaleLinear().domain([0,10]).range(["white","#4169e1"]);
// Pie Chart parameters
var pieMargin = {top:25, right:25, bottom:25, left: 25},
pieWidth = 350 - pieMargin.right - pieMargin.left,
pieHeight = 350 - pieMargin.top - pieMargin.bottom;
var outerRadius = Math.min(pieWidth, pieHeight) / 2,
innerRadius = outerRadius * .50;
var pieColor = d3.scaleOrdinal(['#42b9f4','#3791f2','#374ff1','#25b22e','#107222']);
var legendRectSize = 18, legendSpacing = 4;
var enterAntiClockwise = {startAngle: Math.PI * 2, endAngle: Math.PI * 2};
// Top Item Parameters
var topItemMargin = {top:25, right:50, bottom: 25, left: 25};
var topItemWidth = 350 - topItemMargin.left - topItemMargin.right,
topItemHeight = 350 - topItemMargin.top - topItemMargin.bottom;
var topItemXScale = d3.scaleLinear().range([0, topItemWidth-5]);
var barHeight = 25, barSpacing = 5;
/* SVG */
var svgItemLayout = d3.select("#item_layout")
.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 + ")");
var svgPieChart = d3.select("#pie_chart")
.append("svg")
.attr("width", pieWidth + pieMargin.left + pieMargin.right)
.attr("height", pieHeight + pieMargin.top + pieMargin.bottom)
.append("g")
.attr("transform", "translate(" + (outerRadius + pieMargin.left) + "," + (outerRadius + pieMargin.top) + ")") ;
var svgTopItems = d3.select("#top_items")
.append("svg")
.attr("width", topItemWidth + topItemMargin.left + topItemMargin.right)
.attr("height", topItemHeight + topItemMargin.top + topItemMargin.bottom)
.append("g")
.attr("transform", "translate(" + topItemMargin.left + "," + topItemMargin.top + ")");
/* DRAW FUNCTIONS */
// a single function to draw
function drawItemLayout(data){
var x_offset = 5, y_offset = 5;
x.domain(d3.extent(data, function(d) { return d.x_pos; }));
y.domain(d3.extent(data, function(d) { return d.y_pos; }));
var colorScale = d3.scaleLinear()
.domain([0,d3.max(data, function(d) { return d.sales; })])
.range(["white","#4169e1"]);
// create an update selection with a key function
var g_sel = svgItemLayout
.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", 3)
.attr("ry", 3)
//.style("fill", function(d){ return colorScale(d.sales); })
.style("fill-opacity", 0.8);
// 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);
/* $('rect').tipsy({
gravity: 'w',
html: true,
title: function() {
var d = this.__data__;
return 'Sales: '+d.sales;
}
});*/
// move them into position with transition
g_sel
.transition()
.duration(1000)
.delay(function(d, i) { return i *40; })
.attr("transform", function(d){
return "translate(" + (x(d.x_pos) + x_offset) + "," + (y(d.y_pos) + y_offset) + ")";
})
.style("fill", function(d){ return colorScale(d.sales); });
}
function drawPieChart(data) {
var cf = crossfilter(data);
var salesDimension = cf.dimension(function(d){ return d.sales; });
var categoryDimension = cf.dimension(function(d){ return d.category_name; });
var categoryGroup = categoryDimension.group();
function reduceInitial(p, v) {
return {
sales : 0,
count : 0
};
}
function reduceAdd(p, v) {
p.sales = p.sales + v.sales;
p.count = p.count + 1;
return p;
}
function reduceRemove(p, v) {
p.sales = p.sales - v.sales;
p.count = p.count - 1;
return p;
}
categoryAggregated = categoryGroup.reduce(reduceAdd, reduceRemove, reduceInitial).all();
console.log(categoryAggregated);
var arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.pie()
.value(function(d) { return d.value.sales; })
.sort(null);
var path = svgPieChart.selectAll('path')
.data(pie(categoryAggregated), function(d){ return d.data.key; });
path
.enter()
.append('path')
.attr('d', arc(enterAntiClockwise))
.attr('fill', function(d, i) { return pieColor(i); })
.each(function(d){ this._current = d; }); // store the initial values
path.exit()
.transition()
.duration(750)
.attrTween('d', arcTweenOut)
.remove();
path
.transition()
.duration(1000)
.attrTween('d', arcTween);
// remove old legend:
svgPieChart.selectAll(".legend").remove();
// Add a legend:
var legend = svgPieChart.selectAll('.legend')
.data(pieColor.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * pieColor.domain().length / 2;
var horz = -3 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', pieColor)
.style('stroke', pieColor);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { console.log(categoryAggregated[d].key);return categoryAggregated[d].key; });
// TRANSITION FUNCTIONS
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
// Interpolate exiting arcs start and end angles to Math.PI * 2
// so that they 'exit' at the end of the data
function arcTweenOut(a) {
var i = d3.interpolate(this._current, {startAngle: Math.PI * 2, endAngle: Math.PI * 2, value: 0});
this._current = i(0);
return function (t) {
return arc(i(t));
};
}
}
function drawTopItems (data) {
var cf = crossfilter(data);
var salesDimension = cf.dimension(function(d){ return d.sales; });
topData = salesDimension.top(10);
topItemXScale
.domain([0,d3.max(topData, function(d) { return d.sales; })]);
var g_sel = svgTopItems.selectAll("g")
.data(topData, function(d){ return d.item_name; });
g_sel.exit().remove();
var g_ent = g_sel
.enter()
.append("g");
// UPDATE + ENTER selection
g_sel = g_ent
.merge(g_sel);
g_ent
.append("rect")
.attr("class", "dot")
.attr("width", function(d){ return topItemXScale(d.sales); })
.attr("height", barHeight)
.style("fill","#3791f2")
.style("fill-opacity", 0.8);
// add our text to our g
var labelTxt = g_ent
.append("text")
.attr("font-size", 12)
.attr("text-anchor", "left")
.attr("fill", "black")
.attr("dy", barHeight/2)
.text(function(d){ return d.item_name; })
.attr("transform", function(d, i){
return "translate("+(topItemWidth)+", 0)";
});
var valueTxt = g_ent
.append("text")
.attr("font-size", 14)
.attr("text-anchor", "left")
.attr("fill", "white")
.attr("dy", 2*barHeight/3)
.text(function(d){ return d.sales; })
.attr("transform", function(d, i){
return "translate("+(0.9*topItemXScale(d.sales))+", 0)";
});
g_sel
.transition()
.duration(1200)
.delay(function(d, i) { return i *40; })
.attr("transform", function(d, i){
return "translate(0, "+(i*(barHeight + barSpacing))+")";
})
.attr("width", function(d){ return topItemXScale(d.sales); });
}
/* UTILITY FUNCTIONS */
function castData(data) {
data.forEach(function(d) {
d.item_name = d.item;
d.x_pos = +d.x_pos;
d.y_pos = +d.y_pos;
d.fixture_id = +d.fixture_id;
d.db_id = +d.db_id;
d.campaign_id = +d.campaign_id;
d.sales = +d.sum_units_sold;
d.category_name = d.category;
});
return data;
}
function filterData(data, campaignId, fixtureId) {
data = castData(data);
data = data.filter( function(d){
return (d.campaign_id === campaignId && d.fixture_id === fixtureId);
});
return data;
}
/* MAIN */
var data = d3.csvParse( d3.select("pre#data").text());
// pre-filter data
data = filterData(data, 1550, 10987018);
drawItemLayout(data);
drawPieChart(data);
drawTopItems(data);
/* UPDATE DATA */
function updateData(campaignId, fixtureId) {
var data = d3.csvParse( d3.select("pre#data").text());
// pre-filter data
data = filterData(data, campaignId, fixtureId);
drawItemLayout(data);
drawPieChart(data);
drawTopItems(data);
}
/* GET SELECTION */
$("#select_date_div").change(function(){
var date = $("#select_date_div :selected").val();
var fixtureId = $("#select_fixture_div :selected").val();
date = +date;
fixtureId = +fixtureId;
updateData(date, fixtureId);
});
$("#select_fixture_div").change(function(){
var date = $("#select_date_div :selected").val();
var fixtureId = $("#select_fixture_div :selected").val();
date = +date;
fixtureId = +fixtureId;
updateData(date, fixtureId);
});
我觉得我理解如何为我在网上找到的简单示例实现转换。对于这样的示例,我试图更新给定<g>
的多个元素,但有一些我不太了解。
我希望在如何解决这些具体问题方面得到一些反馈,以及在概念上我对这些转换的工作方式缺少什么,特别是当图表的多个方面发生变化时。