D3.js饼图动态更新转换未按预期工作
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了D3.js饼图动态更新转换未按预期工作相关的知识,希望对你有一定的参考价值。
我很难让我的饼图/圆环图动态更新数据。我对其进行了配置,以便用户滑动范围输入以选择他/她想要查看的数据月份,然后将数据传递给我的d3视觉。为简单起见,我在我的示例中对数据进行了硬编码。您可以查看下面的代码段:
var width = 450;
var height = 350;
var margins = { left: 0, top: 0, right: 0, bottom: 0 };
var svg = d3.select("body")
.append("svg")
.attr("width", width+margins.right)
.attr("height", height+margins.top);
var period = ['JAN 2016','FEB 2016','MAR 2016', 'APR 2016', 'MAY 2016', 'JUN 2016',
'JUL 2016', 'AUG 2016', 'SEP 2016', 'OCT 2016', 'NOV 2016', 'DEC 2016'];
d3.select('#timeslide').on('input', function() {
update(+this.value);
});
function update(value) {
document.getElementById('range').innerhtml=period[value];
create_pie(period[value]);
}
var pie_data = {
'JAN2016': [16,4,1,30],
'FEB2016': [17,4,0,30],
'MAR2016': [16,5,1,29],
'APR2016': [17,4,1,29],
'MAY2016': [17,4,1,29],
'JUN2016': [17,4,2,28],
'JUL2016': [18,3,2,28],
'AUG2016': [18,3,2,28],
'SEP2016': [18,3,2,28],
'OCT2016': [18,3,3,27],
'NOV2016': [18,3,3,27],
'DEC2016': [18,3,3,27]
}
function create_pie(month) {
var radius = Math.min(width, height) / 4;
var color = d3.scale.ordinal().range(['darkblue','steelblue','blue', 'lightblue']);
var arc = d3.svg.arc()
.innerRadius(radius - 50)
.outerRadius(radius - 20);
var sMonth = String(month).replace(' ','');
var pData = pie_data[sMonth];
var pie = d3.layout.pie()
.padAngle(.05)
.value(function(d) { return d; })
.sort(null);
var pieG = svg.append('g')
.attr('transform', 'translate(' + 100 +',' + 75 + ')');
var Ppath = pieG.datum(pData).selectAll(".pie")
.data(pie);
Ppath
.enter().append("path").attr('class','pie');
Ppath
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) {
this.x0 = d.x;
this.dx0 = d.dx;
});
Ppath
.transition()
.duration(650)
.attrTween("d", arcTweenUpdate);
Ppath
.exit().remove();
function arcTweenUpdate(a) {
var i = d3.interpolate({x: this.x0, dx: this.dx0}, a);
return function(t) {
var b = i(t);
this.x0 = b.x;
this.dx0 = b.dx;
return arc(b);
};
}
}
create_pie('JAN 2016');
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.min.js"></script>
</head>
<body>
<div id='sliderContainer'>
<input id='timeslide' type='range' min='0' max='11' value='0' step='1'/><br>
<span id='range'>JAN 2016</span>
</div>
好消息是饼图每次更新月份时都会获取新数据,因为看起来饼图确实在移动。坏消息是饼图看起来非常生涩,似乎我的.transition().duration(650)
根本不起作用。实际上我开始认为饼图一次又一次地被绘制,因为每次更新数据时饼图看起来都比较模糊。我不确定为什么会这样,因为我特别小心地将Ppath.exit().remove();
包含在可能正确的位置。最后,我感觉我对如何动态更新饼图数据的理解基本上是有缺陷的。
我很快就意识到我不是第一个遇到馅饼转换问题的人。最棘手的事情似乎是arcTween
部分。我看着http://stackoverflow.com/questions/22312943/d3-sunburst-transition-given-updated-data-trying-to-animate-not-snap,尽可能地跟着他走。我使用了arcTweenUpdate
的实现,但不幸的是我的情况没有改善。您可能会从片段中注意到彩色弧线在移动,但在饼图上“划分”或“切片”的空白区域是静态的。这不是我想要的,它应该都是过渡,美好和顺利。不应该有像现在这样的静态部件或笨拙的过渡部件。
问题:如何保持视觉的动态特性(通过我的函数pie_data
以当前格式访问create_pie
),还有像M. Bostock的classic donut一样平滑,干净的过渡?
注意:M。Bostock的块使用change()
函数来更新饼图。我更喜欢纠正/增加/增加我现有代码结构的答案(即Ppath.enter()
... Ppath.transition()
... Ppath.exit().remove()
)但是,我愿意接受类似于M. Bostock的orginial的change()
函数,如果有人可以明确解释为什么根据这篇文章我的方法是不可能/非常不切实际的。
编辑
当我尝试动态更新数据时,另一个不可思议的问题是关于单选按钮。根据Karan3112的formatData()
功能:
function formatData(data) {
if (document.getElementById("radioButton1").checked) {
return data[Object.keys(data)[0]].slice(0,4).map(function(item, index) {
let obj = {};
Object.keys(data).forEach(function(key) {
obj[key] = data[key][index] //JAN2016 : [index]
})
return obj;
})
}
else if (document.getElementById("radioButton2").checked) {
return data[Object.keys(data)[0]].slice(5,8).map(function(item, index) {
let obj = {};
Object.keys(data).forEach(function(key) {
obj[key] = data[key][index] //JAN2016 : [index]
})
return obj;
})
}
}
基本上,在我的实际数据中,每个月都有一个长度为8的数组,如下所示:
'JAN2016': [17,4,1,29,7,1,1,42],
因此,根据选中的单选按钮,我希望根据radioButton1
数组中的前4项或radioButton2
数组中的最后4项来绘制饼图。
我最初为我的OP省略了这部分任务,因为我觉得它很容易适应,但是在尝试了很少的进展之后,我已经重新考虑了。我的切片似乎没有用。我认为这是因为formatData
函数只被调用一次。我尝试在formatData
函数中放入一个change()
调用,但这也没有用。
以下示例by Mike Bostock已更新您的代码,如下所示。
- 添加了一个数据格式函数,它将以
{label : value}
格式返回数据。 - 更新了代码逻辑,从加载/重绘onUpdate数据到更新饼图值。
var width = 450;
height = 350;
radius = Math.min(width, height) / 4;
var color = d3.scale.ordinal().range(['darkblue','steelblue','blue', 'lightblue']);
var pie = d3.layout.pie()
.value(function(d) { return d['JAN2016']; })
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 50)
.outerRadius(radius - 20);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr('transform', 'translate(' + 100 +',' + 75 + ')');
var period = ['JAN 2016','FEB 2016','MAR 2016', 'APR 2016', 'MAY 2016', 'JUN 2016',
'JUL 2016', 'AUG 2016', 'SEP 2016', 'OCT 2016', 'NOV 2016', 'DEC 2016'];
var pie_data = {
'JAN2016': [16,4,1,30],
'FEB2016': [17,4,0,30],
'MAR2016': [16,5,1,29],
'APR2016': [17,4,1,29],
'MAY2016': [17,4,1,29],
'JUN2016': [17,4,2,28],
'JUL2016': [18,3,2,28],
'AUG2016': [18,3,2,28],
'SEP2016': [18,3,2,28],
'OCT2016': [18,3,3,27],
'NOV2016': [18,3,3,27],
'DEC2016': [18,3,3,27]
};
var path = svg.datum(formatData(pie_data)).selectAll("path")
.data(pie)
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) { this._current = d; }); // store the initial angles
d3.select('#timeslide').on('input', function() {
change(this.value);
});
function change(key) {
var value = period[key].replace(' ','');
document.getElementById('range').innerHTML=period[key];
pie.value(function(d) { return d[value]; }); // change the value function
path = path.data(pie); // compute the new angles
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function formatData(data){
return data[Object.keys(data)[0]].map(function(item, index){
let obj = {};
Object.keys(data).forEach(function(key){
obj[key] = data[key][index] //JAN2016 : [index]
})
return obj;
})
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<div id='sliderContainer'>
<input id='timeslide' type='range' min='0' max='11' value='0' step='1'/><br>
<span id='range'>JAN 2016</span>
</div>
以上是关于D3.js饼图动态更新转换未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章