我希望开发一个弧形环图。每个新数据集都显示为另一个内部的同心环。
http://jsfiddle.net/NYEaX/101/
我试图对数据中的值求和,并使用它来帮助开发比例算法,以确保每个弧永远不会超过一个周期。
这部分管理同心环。
getArc: function(){
var that = this;
var radiusArray = [100, 80];
function getRadiusRing(i){
return that.radius-(i*20);
}
var thickness = 15;
var arc = d3.svg.arc()
.innerRadius(function(d){
return getRadiusRing(d.index);
})
.outerRadius(function(d){
return getRadiusRing(d.index)+thickness;
})
.startAngle(function(d, i){
return d.startAngle;
})
.endAngle(function(d, i){
return d.endAngle;
});
return arc;
}
但我认为结束角度计算存在缺陷。如果您启用更多数据 - 同心环虽然形状正确,但是它们在整个图表周围都有可疑的弧长?
setData: function(data){
var diameter = 2 * Math.PI * this.radius;
var localData = new Array();
var segmentValueSum = 0;
$.each(data[0].segments, function( ri, va) {
segmentValueSum+= va.value;
});
$.each(data[0].segments, function(ri, value) {
var segmentValue = value.value;
var fraction = segmentValue/segmentValueSum;
var arcBatchLength = fraction*2*Math.PI;
var arcPartition = arcBatchLength;
var startAngle = 0;
var endAngle = ((ri+1)*arcPartition);
data[0].segments[ri]["startAngle"] = startAngle;
data[0].segments[ri]["endAngle"] = endAngle;
data[0].segments[ri]["index"] = ri;
});
localData.push(data[0].segments);
return localData[0];
}
尝试将图表构建为如下所示
我已经增强了图表,但仍然存在图例,环和值的放置和更新问题。为什么旧数据仍然存在? http://jsfiddle.net/NYEaX/123/
如果有更多或更少的数据 - 标签无法正确放置
//draw labels
valueLabels = value_group.selectAll("text.value").data(reversedata)
valueLabels.enter().append("svg:text")
.attr("class", "value")
.attr("transform", function(d) {
var rings = counts;
return "translate("+(that.radius+55)/rings+", 0)";
})
.attr("dx", function(d, i){
return 19*i; })
.attr("dy", function(d, i){
return -5;
})
.attr("text-anchor", function(d){
return "start";
}).text(function(d){
return d.value;
});
valueLabels.transition().duration(300).attrTween("d", arcTween)
valueLabels.exit().remove();
答案 0 :(得分:2)
替换
var endAngle = ((ri+1)*arcPartition);
通过
var endAngle = startAngle + arcPartition;
答案 1 :(得分:1)
我已设法修复所有错误 - 这是最终代码作为jquery插件,享受
http://jsfiddle.net/NYEaX/165/
$(document).ready(function() {
(function( $ ){
var methods = {
el: "",
init : function(options) {
var clone = jQuery.extend(true, {}, options["data"]);
var preparedData = methods.setData(clone);
methods.el = this;
methods.setup(preparedData, options["width"], options["height"]);
},
setup: function(data, w, h){
var selector = methods.el["selector"];
var padding = 20;
var chart = d3.select(selector).append("svg:svg")
.attr("class", "chart")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("class", "concentricchart")
.attr("transform", "translate("+((w/3)+padding)+","+h/3+")");
methods.radius = Math.min(w, h) / 2;
var label_group = chart.append("svg:g")
.attr("class", "label_group")
.attr("transform", "translate("+((w/3)-15)+","+(-h/4)+")");
var legend_group = chart.append("svg:g")
.attr("class", "legend_group")
.attr("transform", "translate("+((w/3)-130)+","+((-h/4)-5)+")");
var value_group = chart.append("svg:g")
.attr("class", "value_group")
.attr("transform", "translate(0,"+(h/4)+")");
var path_group = chart.append("svg:g")
.attr("class", "path_group")
.attr("transform", "translate(0,"+(h/4)+")");
this.generateArcs(selector, data);
},
update: function(data){
var clone = jQuery.extend(true, {}, data);
var preparedData = methods.setData(clone);
methods.el = this;
methods.animate(preparedData);
methods.oldData = preparedData;
},
animate: function(data){
var that = this;
var selector = methods.el["selector"];
that.generateArcs(selector, data);
},
setData: function(data){
var diameter = 2 * Math.PI * this.radius;
var localData = new Array();
var segmentValueSum = 0;
$.each(data[0].segments, function( ri, va) {
segmentValueSum+= va.value;
});
segmentValueSum = 200;//consistent total accross different data sets
$.each(data[0].segments, function(ri, value) {
var segmentValue = value.value;
var fraction = segmentValue/segmentValueSum;
var arcBatchLength = fraction*4*Math.PI;
var arcPartition = arcBatchLength;
var startAngle = Math.PI/2;
var endAngle = startAngle + arcPartition;
data[0].segments[ri]["startAngle"] = startAngle;
data[0].segments[ri]["endAngle"] = endAngle;
data[0].segments[ri]["index"] = ri;
});
localData.push(data[0].segments);
return localData[0];
},
textOffset: 10,
generateArcs: function(selector, data){
var that = this;
var chart = d3.select(selector);
//append previous value to it.
$.each(data, function(index, value) {
if(that.oldData[index] != undefined){
data[index]["previousEndAngle"] = that.oldData[index].endAngle;
}
else{
data[index]["previousEndAngle"] = 0;
}
});
var thickness = $(selector).data("thickness");
var ir = ($(selector).data("width")/3);
var path_group = d3.select(selector+ ' .path_group');
var arcpaths = path_group.selectAll("path")
.data(data);
arcpaths.enter().append("svg:path")
.attr("class", function(d, i){
return d.machineType;
})
.style("fill", function(d, i){
return d.color;
})
.transition()
.ease("elastic")
.duration(750)
.attrTween("d", function(d){
return that.arcTween(d, thickness, ir);
});
arcpaths.transition()
.ease("elastic")
.style("fill", function(d, i){
return d.color;
})
.duration(750)
.attrTween("d", function(d){
return that.arcTween(d, thickness, ir);
});
arcpaths.exit().transition()
.ease("bounce")
.duration(750)
.attrTween("d", function(d){
return that.arcTween(d, thickness, ir);
})
.remove();
//draw labels
that.drawLabels(chart, data, ir, thickness);
that.buildLegend(chart, data);
},
arcTween: function(b, thickness, ir){
var that = methods;
var prev = JSON.parse(JSON.stringify(b));
prev.endAngle = b.previousEndAngle;
var i = d3.interpolate(prev, b);
return function(t) {
return that.getArc(thickness, ir)(i(t));
};
},
drawLabels: function(chart, data, ir, thickness){
$(methods.el["selector"]+' .value_group').empty();
var that = this;
var reversedata = data.reverse();
var counts = data.length;
var value_group = d3.select(methods.el["selector"]+ ' .value_group');
valueLabels = value_group.selectAll("text.value").data(reversedata)
valueLabels.enter().append("svg:text")
.attr("class", "value")
.attr("transform", function(d) {
return "translate("+(that.getRadiusRing(ir, counts-1))+", 0)";
})
.attr("dx", function(d, i){
return 20*i; })
.attr("dy", function(d, i){
return -5;
})
.attr("text-anchor", function(d){
return "start";
}).text(function(d){
return d.value;
});
valueLabels
.transition()
.duration(300)
.attrTween("d", function(d){
return that.arcTween(d, thickness, ir);
})
valueLabels
.exit()
.remove();
},
buildLegend: function(chart, data){
console.log("build legend");
$(methods.el["selector"]+' .label_group').empty();
$(methods.el["selector"]+' .legend_group').empty();
var label_group = d3.select(methods.el["selector"]+ ' .label_group');
//draw labels
labels = label_group.selectAll("text.labels")
.data(data.reverse());
labels.enter().append("svg:text")
.attr("class", "labels")
.attr("dy", function(d, i){
return 19*i
})
.attr("text-anchor", function(d){
return "start";
})
.text(function(d){
return d.label;
});
labels.exit().remove();
var legend_group = d3.select(methods.el["selector"]+ ' .legend_group');
legend = legend_group.selectAll("circle").data(data);
legend.enter().append("svg:circle")
.attr("cx", 100)
.attr("cy", function(d, i){
return 19*i
})
.attr("r", 7)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d){
return d.color;
});
legend.exit().remove();
},
getRadiusRing: function(ir, i){
return ir-(i*20);
},
getArc: function(thickness, ir){
var that = this;
var arc = d3.svg.arc()
.innerRadius(function(d){
return that.getRadiusRing(ir, d.index);
})
.outerRadius(function(d){
return that.getRadiusRing(ir+thickness, d.index);
})
.startAngle(function(d, i){
return d.startAngle;
})
.endAngle(function(d, i){
return d.endAngle;
});
return arc;
},
radius: 100,
oldData: ""
};
$.fn.concentric = function(methodOrOptions) {
if ( methods[methodOrOptions] ) {
return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
// Default to "init"
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + methodOrOptions + ' does not exist' );
}
};
})(jQuery);
var dataCharts = [
{
"data": [
{
"segments": [
{
"label": "Turkey",
"value": 25,
"color": "red"
},
{
"label": "United States",
"value": 40,
"color": "blue"
},
{
"label": "Switzerland",
"value": 60,
"color": "green"
},
{
"label": "Iceland",
"value": 80,
"color": "gold"
}
]
}
]
},
{
"data": [
{
"segments": [
{
"label": "Peanut Butter",
"value": 50,
"color": "red"
},
{
"label": "Tea",
"value": 25,
"color": "orange"
},
{
"label": "Cheese",
"value": 25,
"color": "purple"
}
]
}
]
} ,
{
"data": [
{
"segments": [
{
"label": "Jam",
"value": 90,
"color": "purple"
},
{
"label": "Lemons",
"value": 15,
"color": "brown"
}
]
}
]
}
];
var clone = jQuery.extend(true, {}, dataCharts);
//__invoke concentric
$('[data-role="concentric"]').each(function(index) {
var selector = "concetric"+index;
$(this).attr("id", selector);
var options = {
data: clone[0].data,
width: $(this).data("width"),
height: $(this).data("height")
}
$("#"+selector).concentric(options);
});
$(".testers a").on( "click", function(e) {
e.preventDefault();
var clone = jQuery.extend(true, {}, dataCharts);
var min = 0;
var max = 2;
//__invoke concentric
$('[data-role="concentric"]').each(function(index) {
pos = Math.floor(Math.random() * (max - min + 1)) + min;
console.log("id", $(this).attr("id"));
$("#"+$(this).attr("id")).concentric('update', clone[pos].data);
});
});
});
答案 2 :(得分:1)
我制作了一个图表从顶部开始的版本 - 如此不同的方向。最好将其作为变量 - 也许可以选择在4个不同的象限中启动图表。
http://jsfiddle.net/NYEaX/364/
我调整了
设置数据
中的起始角度var startAngle = Math.PI * 2;
- 标签展示位置
valueLabels.enter().append("svg:text")
.attr("class", "value")
.attr("transform", function(d) {
return "translate("+(that.getRadiusRing(ir, counts-1))+", 0)";
})
.attr("dx", function(d, i){
return 0;
})
.attr("dy", function(d, i){
return (thickness + 4)*i;
})
.attr("text-anchor", function(d){
return "start";
}).text(function(d){
return d.value;
});