您的位置:首页 > 其它

利用Canvas绘制雷达图

2017-10-27 21:32 866 查看
雷达图(蜘蛛网图)是一种常见的数据分析图表,本文采用canvas来绘制雷达图,并最终封装成一个小组件。首先来看一下最终的效果图:



如何画正多边形

以正五边形雷达图为例(其他任意正多边形也一样),如下图所示。
Canvas
画图的原点在左上角,以r为半径,
(r, r)
为圆心作圆,作为正五边形的外接圆,则正五边形每条边所对应的圆心角均为
rad = 2*Math.PI/5
。再根据正余弦可以求得每个定点所对应的坐标,这样一个正五边形就可以画出来了。



下面进行具体的代码实现。

HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas绘制雷达图</title>
<style>
body, html, div{
margin: 0;
padding: 0;
}
.container {
width: 300px;
height: 300px;
margin: 50px auto;
}
</style>
</head>
<body>
<div class="container" id="container"></div>
</body>
</html>


基本样式和模拟数据

var container = document.getElementById('container');
var cans = document.createElement("canvas");
container.appendChild(cans);

var ctx = cans.getContext("2d");
var data = [["HTML", 0.5], ["CSS", 0.6], ["JS", 0.4], ["jQuery", 0.8], ["React", 0.7]];
cans.width = 300;
cans.height = 300;
var step = data.length;
var r = 150;


绘制背景网格

将正五边形从内到外分成十份,并以蓝白相间的背景进行填充。

//绘制网格背景
var isBlue = false;
for(var s = 10; s > 0; s--) {
ctx.beginPath();
for(var i=0;i<step;i++) {
var rad = 2*Math.PI/step * i;
var x = r + Math.sin(rad)*r*(s/10);
var y = r + Math.cos(rad)*r*(s/10);
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fillStyle = (isBlue = !isBlue)?'#99c0ff' : '#f1f9ff';
ctx.fill();
}


效果如下:



绘制伞骨

由于项目名称是定位在五边形的顶点处,因此,在绘制伞骨的同时可以把每个项目的名称同时绘制出来。

//绘制伞骨
ctx.beginPath();
for(var i=0;i<step;i++) {
var rad = 2*Math.PI/step * i;
var x = r + Math.sin(rad)*r;
var y = r + Math.cos(rad)*r;
ctx.moveTo(r,r);
ctx.lineTo(x, y);

var text = document.createElement("div");
text.innerHTML = data[i][0];
text.style.position = "absolute";

//添加文本
if(x > r) {
text.style.left = ( x + 10) + 'px';
} else {
text.style.right = (300-x +5) + 'px';
}

if(y > r) {
text.style.top = y + 'px';
} else {
text.style.bottom = (300 - y) + 'px';
}
container.appendChild(text);
}
ctx.strokeStyle = "#e0e0e0"
ctx.stroke();


效果如下:



绘制数据点

ctx.fillStyle = "#ff7676";
for(var i=0;i<step;i++) {
var rad = 2*Math.PI/step * i;
var x = r + Math.sin(rad)*r*data[i][1];
var y = r + Math.cos(rad)*r*data[i][1];
ctx.beginPath();
ctx.arc(x,y,4,0,2*Math.PI);
ctx.fill();
ctx.closePath();
}


效果入下:



绘制折线

ctx.strokeStyle = "#f00";
ctx.beginPath();

for(var i=0;i<step;i++) {
var rad = 2*Math.PI/step * i;
var x = r + Math.sin(rad)*r*data[i][1];
var y = r + Math.cos(rad)*r*data[i][1];
ctx.lineTo(x,y);
}
ctx.closePath();
ctx.stroke();


最终效果图:



封装插件

完整插件代码:

//radar.js
(function() {
var Radar = function(cfg) {
var outContainer = document.querySelector(cfg.el);
var container = document.createElement("div");
var cans = document.createElement("canvas");
container.appendChild(cans);
outContainer.appendChild(container);

var ctx = cans.getContext("2d");
var data = cfg.data;
var w = cfg.width;
var h = cfg.height;
container.style.position = "relative";
container.style.width = w+"px";
container.style.height = h+"px";
cans.width = w;
cans.height = h;

var step = data.length;
var r = w/2;

//绘制网格背景
var isBlue = false;

for(var s = 10; s > 0; s--) {
ctx.beginPath();
for(var i=0;i<step;i++) {
var rad = 2*Math.PI/step * i;
var x = r + Math.sin(rad)*r*(s/10);
var y = r + Math.cos(rad)*r*(s/10);
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fillStyle = (isBlue = !isBlue)?'#99c0ff' : '#f1f9ff';
ctx.fill();
}

//绘制伞骨
ctx.beginPath();
for(var i=0;i<step;i++) {
var rad = 2*Math.PI/step * i;
var x = r + Math.sin(rad)*r;
var y = r + Math.cos(rad)*r;
ctx.moveTo(r,r);
ctx.lineTo(x, y);

var text = document.createElement("div");
text.innerHTML = data[i][0];
text.style.position = "absolute";

//添加文本
if(x > r) {
text.style.left = ( x + 10) + 'px';
} else {
text.style.right = (w-x +5) + 'px';
}

if(y > r) {
text.style.top = y + 'px';
} else {
text.style.bottom = (h - y) + 'px';
}
container.appendChild(text);
}
ctx.strokeStyle = "#e0e0e0"
ctx.stroke();

//绘制折线
ctx.strokeStyle = "#f00"; ctx.beginPath(); for(var i=0;i<step;i++) { var rad = 2*Math.PI/step * i; var x = r + Math.sin(rad)*r*data[i][1]; var y = r + Math.cos(rad)*r*data[i][1]; ctx.lineTo(x,y); } ctx.closePath(); ctx.stroke();

//添加数据点
ctx.fillStyle = "#ff7676"; for(var i=0;i<step;i++) { var rad = 2*Math.PI/step * i; var x = r + Math.sin(rad)*r*data[i][1]; var y = r + Math.cos(rad)*r*data[i][1]; ctx.beginPath(); ctx.arc(x,y,4,0,2*Math.PI); ctx.fill(); ctx.closePath(); }

}
window["Radar"] = Radar;
})();


调用:

var radar = new Radar({
el: "#container",
width: 300,
height: 300,
data: [["HTML", 0.5], ["CSS", 0.6], ["JS", 0.9], ["jQuery", 0.8], ["React", 0.7]]
});


总结

到现在为止,整个雷达图就绘制完毕。当然还可以添加更多的内容,比如文字颜色,动画等。文字颜色可以在data数组中添加,给每一项再增加一个颜色项。动画的话可以将数据点和折线部分提出来单独绘制一个图层,通过控制数据点的大小实现图形的生长动画。完整版参加github:https://github.com/materialcoder/Chart-Component
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  canvas 雷达图