d3.js同心环图 [英] d3.js concentric ring chart
问题描述
我在寻找一个弧形环图。
$
如果数据多或少, //绘制标签
valueLabels = value_group.selectAll(text.value)。data(reversedata)
valueLabels.enter()。append(svg:text)
.attr class,value)
.attr(transform,function(d){
var rings = counts;
returntranslate radius + 55)/ rings +,0);
})
.attr(dx,function(d,i){
return 19 * i;})
.attr(dy,function(d,i){
return -5;
})
.attr(text-anchor,function(d){
returnstart;
})text(function(d){
return d.value;
});
valueLabels.transition()。duration(300).attrTween(d,arcTween)
valueLabels.exit()。remove
我已经设法修复所有的错误 - 是最终的代码作为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;
方法。 setup(prepareData,options [width],options [height]);
},
setup:function(data,w,h){
= 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; //一致的总数跨越不同的数据集
$ .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);
//将上一个值追加到它。
$ .each(data,function(index,value){
if(that.oldData [index]!= undefined){
data [index] [previousEndAngle] = 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){
return that.arcTween(d,thickness,ir);
})
.remove();
//绘制标签
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')。
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){
returntranslate(+(that.getRadiusRing(ir,counts-1))+ );
})
.attr(dx,function(d,i){
return 20 * i;})
.attr (d,i){
return -5;
})
.attr(text-anchor,function(d){
returnstart;
})。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');
//绘制标签
labels = label_group.selectAll(text.labels)
.data(data.reverse());
labels.enter()。append(svg:text)
.attr(class,labels)
.attr d,i){
return 19 * i
})
.attr(text-anchor,function(d){
returnstart;
})
.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 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(thick,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 ){
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){
//默认为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 ,
value:80,
color:gold
}
]
}
]
} b $ b {
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]')每个函数(索引){
var selector =concetric ;
$(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 $每个函数(索引){
pos = Math.floor(Math.random()*(max - min + 1))+ b $ b $('[data-role =concentric min;
console.log(id,$(this).attr(id));
$(#+ $(this).attr(id))。同心('update',clone [pos] .data);
});
});
});
I am looking to develop an arc ring chart. Where each new set of data is displayed as a concentric ring inside the other.
http://jsfiddle.net/NYEaX/101/
I've tried to take a sum of the values in the data, and use this to help develop a proportion algorithm, to ensure each arc never does more than one cycle.
this part manages the concentric ring.
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;
}
but I think there is a flaw in the end angle calculations. If you enable more data - the concentric rings although they form correctly have suspicious arc lengths where they wrap around the entire chart?
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];
}
trying to build the chart to look like the following
I've enhanced the chart, but still have issues with the placement and updating of the legend, rings and values. Why is old data remaining? http://jsfiddle.net/NYEaX/123/
if there is more or less data - the labels don't get placed correctly
//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();
I've managed to fix all the bugs - Here is the final code as a jquery plugin, enjoy
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);
});
});
});
这篇关于d3.js同心环图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!