<!DOCTYPE html>
<html>
<head>
<script data-require="[email protected]" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
.big.radarChart {
width: 500px;
height: 400px;
margin: 20px;
}
#chart-radar1 {
margin-bottom: 60px;
}
</style>
</head>
<body>
<button id="update">Update</button>
<div class="big radarChart" id="chart-radar" ></div>
<script>
var wMaior = 400;
var wMenor = 200;
var colorscale = d3.scale.category10();
var legendOptions = ['Legend 1'];
var size = 2;
if (size > 0) {
var json = [
[{
"axis": "A",
"value": 6
}, {
"axis": "B",
"value": 4
}, {
"axis": "C",
"value": 6
}, {
"axis": "D",
"value": 5.5
}, {
"axis": "E",
"value": 8
}, {
"axis": "F",
"value": 7
}, {
"axis": "G",
"value": 9
}, {
"axis": "H",
"value": 10
}, {
"axis": "I",
"value": 3.5
}]
];
}
function drawRadarCharts() {
drawRadarChart('#chart-radar', wMaior, wMaior);
};
function drawRadarChart(divId, w, h) {
var textSizeLevels = "10px !important";
var textSizeTooltip = "13px !important";
var textSizeLegend = "11px !important";
var circleSize = 5;
var strokeWidthPolygon = "2px";
var RadarChart = {
draw: function(id, data, options) {
var cfg = {
radius: circleSize,
w: w,
h: h,
factor: 1,
factorLegend: .85,
levels: 3,
maxValue: 0,
radians: 2 * Math.PI,
opacityArea: 0.001,
ToRight: 5,
TranslateX: 80,
TranslateY: 30,
ExtraWidthX: 10,
ExtraWidthY: 100,
color: d3.scale.category10()
};
if ('undefined' !== typeof options) {
for (var i in options) {
if ('undefined' !== typeof options[i]) {
cfg[i] = options[i];
}
}
}
cfg.maxValue = Math.max(cfg.maxValue, d3.max(data, function(i) {
return d3.max(i.map(function(o) {
return o.value;
}));
}));
var allAxis = (data[0].map(function(i, j) {
return i.axis;
}));
var total = allAxis.length;
var radius = cfg.factor * Math.min(cfg.w/2, cfg.h/2);
var svg = d3.select(id).select('svg'),
polyPoints = null;
if (svg.node()){
polyPoints = svg.select("polygon").attr("points");
svg.remove();
}
var g = d3.select(id)
.append("svg")
.attr("width", cfg.w + cfg.ExtraWidthX)
.attr("height", cfg.h + cfg.ExtraWidthY)
.attr("class", "graph-svg-component")
.append("g")
.attr("transform", "translate(" + cfg.TranslateX + "," + cfg.TranslateY + ")");
var tooltip;
// Circular segments
for (var j = 0; j < cfg.levels - 1; j++) {
var levelFactor = cfg.factor * radius * ((j + 1)/cfg.levels);
g.selectAll(".levels")
.data(allAxis)
.enter()
.append("svg:line")
.attr("x1", function(d, i) {
return levelFactor * (1 - cfg.factor * Math.sin(i * cfg.radians/total));
})
.attr("y1", function(d, i) {
return levelFactor * (1 - cfg.factor * Math.cos(i * cfg.radians/total));
})
.attr("x2", function(d, i) {
return levelFactor * (1 - cfg.factor * Math.sin((i + 1) * cfg.radians/total));
})
.attr("y2", function(d, i) {
return levelFactor * (1 - cfg.factor * Math.cos((i + 1) * cfg.radians/total));
})
.attr("class", "line")
.style("stroke", "grey")
.style("stroke-opacity", "0.75")
.style("stroke-width", "0.3px")
.attr("transform", "translate(" + (cfg.w/2 - levelFactor) + ", " + (cfg.h/2 - levelFactor) + ")");
}
// Text indicating at what % each level is
for (var j = 0; j < cfg.levels; j++) {
var levelFactor = cfg.factor * radius * ((j + 1)/cfg.levels);
g.selectAll(".levels")
.data([1]) //dummy data
.enter()
.append("svg:text")
.attr("x", function(d) {
return levelFactor * (1 - cfg.factor * Math.sin(0));
})
.attr("y", function(d) {
return levelFactor * (1 - cfg.factor * Math.cos(0));
})
.attr("class", "legend")
.style("font-family", "sans-serif")
.style("font-size", textSizeLevels)
.attr("transform", "translate(" + (cfg.w/2 - levelFactor + cfg.ToRight) + ", " + (cfg.h/2 - levelFactor) + ")")
.attr("fill", "#737373")
.text((j + 1) * cfg.maxValue/cfg.levels);
}
series = 0;
var axis = g.selectAll(".axis")
.data(allAxis)
.enter()
.append("g")
.attr("class", axis);
axis.append("line")
.attr("x1", cfg.w/2)
.attr("y1", cfg.h/2)
.attr("x2", function(d, i) {
return cfg.w/2 * (1 - cfg.factor * Math.sin(i * cfg.radians/total));
})
.attr("y2", function(d, i) {
return cfg.h/2 * (1 - cfg.factor * Math.cos(i * cfg.radians/total));
})
.attr("class", "line")
.style("stroke", "grey")
.style("stroke-width", "1px");
axis.append("text")
.attr("class", "legend")
.text(function(d) {
return d;
})
.style("font-family", "sans-serif")
.style("font-size", textSizeLegend)
.attr("text-anchor", "middle")
.attr("dy", "1.5em")
.attr("transform", function(d, i) {
return "translate(0, -10)";
})
.attr("x", function(d, i) {
return cfg.w/2 * (1 - cfg.factorLegend * Math.sin(i * cfg.radians/total)) - 60 * Math.sin(i * cfg.radians/total);
})
.attr("y", function(d, i) {
return cfg.h/2 * (1 - Math.cos(i * cfg.radians/total)) - 20 * Math.cos(i * cfg.radians/total);
});
data.forEach(function(y, x) {
dataValues = [];
g.selectAll(".nodes")
.data(y, function(j, i) {
dataValues.push([
cfg.w/2 * (1 - (parseFloat(Math.max(j.value, 0))/cfg.maxValue) * cfg.factor * Math.sin(i * cfg.radians/total)),
cfg.h/2 * (1 - (parseFloat(Math.max(j.value, 0))/cfg.maxValue) * cfg.factor * Math.cos(i * cfg.radians/total))
]);
});
dataValues.push(dataValues[0]);
g.selectAll(".area")
.data([dataValues])
.enter()
.append("polygon")
.attr("points", function(d){
if (polyPoints)
return polyPoints;
else
return d3.range(d.length).map(function(){
return (cfg.w/2) + "," + (cfg.h/2)
}).join(" ");
})
.attr("class", "radar-chart-series_" + series)
.style("stroke-width", strokeWidthPolygon)
.style("stroke", cfg.color(series))
.style("fill-opacity", cfg.opacityArea)
.on('mouseover', function(d) {
z = "polygon." + d3.select(this).attr("class");
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", 0.1);
g.selectAll(z)
.transition(200)
.style("fill-opacity", 0.7);
})
.on('mouseout', function() {
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", cfg.opacityArea);
})
.transition()
.duration(2000)
.attr("points", function(d) {
var str = "";
for (var pti = 0; pti < d.length; pti++) {
str = str + d[pti][0] + "," + d[pti][1] + " ";
}
return str;
})
.style("fill", function(j, i) {
return cfg.color(series);
})
series++;
});
series = 0;
data.forEach(function(y, x) {
var c = g.selectAll(".nodes")
.data(y).enter()
.append("svg:circle")
.attr("class", "radar-chart-series_" + series)
.attr('r', cfg.radius)
.attr("alt", function(j) {
return Math.max(j.value, 0);
})
.attr("cx", function(j, i) {
dataValues.push([
cfg.w/2 * (1 - (parseFloat(Math.max(j.value, 0))/cfg.maxValue) * cfg.factor * Math.sin(i * cfg.radians/total)),
cfg.h/2 * (1 - (parseFloat(Math.max(j.value, 0))/cfg.maxValue) * cfg.factor * Math.cos(i * cfg.radians/total))
]);
return cfg.w/2 * (1 - (Math.max(j.value, 0)/cfg.maxValue) * cfg.factor * Math.sin(i * cfg.radians/total));
})
.attr("cy", function(j, i) {
return cfg.h/2 * (1 - (Math.max(j.value, 0)/cfg.maxValue) * cfg.factor * Math.cos(i * cfg.radians/total));
})
.attr("data-id", function(j) {
return j.axis;
})
.style("fill", cfg.color(series))
.style("fill-opacity", 0)
.on('mouseover', function(d) {
newX = parseFloat(d3.select(this).attr('cx')) - 10;
newY = parseFloat(d3.select(this).attr('cy')) - 5;
tooltip.attr('x', newX)
.attr('y', newY)
.text(d.value)
.transition(200)
.style('opacity', 1);
z = "polygon." + d3.select(this).attr("class");
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", 0.1);
g.selectAll(z)
.transition(200)
.style("fill-opacity", 0.7);
})
.on('mouseout', function() {
tooltip.transition(200)
.style('opacity', 0);
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", cfg.opacityArea);
});
c.transition()
.delay(1750)
.duration(100)
.style("fill-opacity", 0.9);
c.append("svg:title")
.text(function(j) {
return Math.max(j.value, 0);
});
series++;
});
//Tooltip
tooltip = g.append('text')
.style('opacity', 0)
.style('font-family', 'sans-serif')
.style('font-size', textSizeTooltip);
}
};
// Options for the Radar chart, other than default
var myOptions = {
w: w,
h: h,
ExtraWidthX: 180,
labelScale: 0.7,
levels: 5,
levelScale: 0.85,
facetPaddingScale: 1.9,
maxValue: 0.6,
showAxes: true,
showAxesLabels: true,
showLegend: true,
showLevels: true,
showLevelsLabels: false,
showPolygons: true,
showVertices: true
};
RadarChart.draw(divId, json, myOptions);
////////////////////////////////////////////
/////////// Initiate legend ////////////////
////////////////////////////////////////////
var svg = d3.select('#chart-radar')
.selectAll('svg')
.append('svg')
.attr("width", w + 300)
.attr("height", h)
.style("font-size", textSizeLegend);
// Initiate Legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("height", 100)
.attr("width", 200)
.attr('transform', 'translate(90,20)');
// Create colour squares
legend.selectAll('rect')
.data(legendOptions)
.enter()
.append("rect")
.attr("x", w - 8)
.attr("y", function(d, i) {
return i * 20;
})
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d, i) {
return colorscale(i);
});
// Create text next to squares
legend.selectAll('text')
.data(legendOptions)
.enter()
.append("text")
.attr("x", w + 3)
.attr("y", function(d, i) {
return i * 20 + 9;
})
.attr("font-size", textSizeLegend)
.attr("fill", "#737373")
.text(function(d) {
return d;
});
};
function update() {
console.log("here");
json = [
[{
"axis": "A",
"value": 4
}, {
"axis": "B",
"value": 13
}, {
"axis": "C",
"value": 8
}, {
"axis": "D",
"value": 15
}, {
"axis": "E",
"value": 2
}, {
"axis": "F",
"value": 5
}, {
"axis": "G",
"value": 9
}, {
"axis": "H",
"value": 3
}, {
"axis": "I",
"value": 1
}]
];
drawRadarChart('#chart-radar', wMaior, wMaior);
};
drawRadarCharts();
d3.select("button").on("click", update);
</script>
</html>
嗨馬克,我知道這可能是一個長期的!但我希望你能提供一個見解,說明爲什麼它不適用於多個數據輸入? ([see fiddle](http://jsfiddle.net/j8g8s6Lz/3/))。我嘗試過調試,而且我發現的唯一的事情是當只有一組數據要繪製時,數組的長度是13,但是當有兩組時,第一組點數組是7和第二十九。當我猜測它應該是13/13?我不確定如果我有什麼想法我在。希望我可以自己弄清楚,我認爲在另一個問題中提出這個問題並不合適! – lolio
@Iolio,你的代碼中有很多東西不對。首先,最大的是可變範圍問題。你正在依賴全局變量,這讓你感到沮喪。例如,在你的更新中,你創建一個名爲'json'的** local **變量,然後調用你的重繪函數。由於這個重新分配是本地的,它不會影響全局的'json'變量。你應該把這個變量傳遞給你的繪圖... – Mark
@Iolio,其次,你正在做大量的顯式循環你的數據,而'd3'完全是關於沒有顯式循環的綁定。這會給你帶來麻煩,特別是在你的'.append(「svg:circle」)'你重新推入數據值的東西(這就是爲什麼你在一個數組中得到19的原因)... – Mark