01#Web 实战:雷达图
成品演示

绘制雷达图
雷达图里外层
function calcPolygonX(radarX, radius, increaseAngle) {
return radarX + radius * Math.cos(increaseAngle);
}
function calcPolygonY(radarY, radius, increaseAngle) {
return radarY - radius * Math.sin(increaseAngle);
}
/**
* @param radarMapTotalSides 雷达图有多少面,5 就是五面
* @param radius 每一层圆的半径长度,步数
* @param radarX 雷达图圆心在 Canvas 的 x 轴坐标
* @param radarY 雷达图圆心在 Canvas 的 y 轴坐标
* @param ctx Canvas 对象
*/
function drawPolygon(radarMapTotalSides, radius, radarX, radarY, ctx) {
let averageAngle = (Math.PI * 2) / radarMapTotalSides;
let increaseAngle = 0;
let targetX, targetY;
ctx.beginPath();
for (let i = 0; i < radarMapTotalSides; i++) { // 绘制当前层的面
targetX = calcPolygonX(radarX, radius, increaseAngle);
targetY = calcPolygonY(radarY, radius, increaseAngle);
ctx.lineTo(targetX, targetY);
increaseAngle += averageAngle;
}
ctx.closePath();
ctx.stroke();
}
/**
* @param radarLayers 雷达图层数
* @param polygonPerStep 每一层圆的半径长度,步数
* @param radarMapTotalSides 雷达图有多少面,5 就是五面
* @param radarX 雷达图圆心在 Canvas 的 x 轴坐标
* @param radarY 雷达图圆心在 Canvas 的 y 轴坐标
* @param ctx Canvas 对象
*/
function drawRadarMap(radarLayers, polygonPerStep, radarMapTotalSides, radarX, radarY, ctx) {
let radius = polygonPerStep;
for (let j = 0; j < radarLayers; j++) { // 绘制 radarLayers 层的雷达图
drawPolygon(radarMapTotalSides, radius, radarX, radarY, ctx);
radius = radius + polygonPerStep;
}
}
通过以上代码,就可以绘制雷达图的基本框架,基于此基础之上,我们可以继续往下完善:

连接雷达图里外层
第一层的面与面之间的交界处与第二层的相连就可以形成一条线,而我们雷达图上的每一个节点就是处于这一条线上的。
保存点坐标
drawPolygon()函数是绘制雷达图的主要函数,函数里面循环了我们指定多少层雷达图,每一层雷达图的每一个面与面之间的交界处,即点的坐标都需要被保存,才能够在我们后续过程中为雷达图添加连线。
在drawRadarMap()函数中声明一个 axis 数组,保存雷达图每一层的交界点的 x、y 坐标信息。下面的代码演示中省略了重复的代码:
function drawPolygon(radarMapTotalSides, currentPolygonLayer, radius, radarX, radarY, axis, ctx) {
// ...
ctx.beginPath();
axis.push({ layer: currentPolygonLayer, coords: [] }); // 保存点坐标的数组
for (let i = 0; i < radarMapTotalSides; i++) { // 绘制当前层的面
// ...
axis[currentPolygonLayer].coords.push({ x: targetX, y: targetY }); // 添加点坐标到数组中
}
ctx.closePath();
ctx.stroke();
}
function drawRadarMap(radarLayers, polygonPerStep, radarMapTotalSides, radarX, radarY, ctx) {
let axis = []; // 用于保存每一个多边形的每一个点坐标
// ...
for (let j = 0; j < radarLayers; j++) { // 绘制 radarLayers 层的雷达图
drawPolygon(radarMapTotalSides, j, radius, radarX, radarY, axis, ctx);
// ...
}
}
axis 的数组元素是一个对象,对象包含两个字段:layer 和 coords。layer 代表当前的雷达图索引,即第几层;coords 保存当前层数的每一个交界点的 x、y 坐标轴信息。
绘制直线函数
有了每一层的交界点的点坐标信息,我们就可以从里到外依次连线了。连线只需要最外层的交界点坐标信息,把圆心到每一个交界点连成一条直线。专门写一个函数drawStria:
function drawStria(radarMapTotalSides, axis, radarX, radarY, ctx) {
let coords = axis[axis.length - 1].coords;
for (let i = 0; i < radarMapTotalSides; i++) {
ctx.beginPath();
ctx.moveTo(radarX, radarY);
ctx.lineTo(coords[i].x, coords[i].y);
ctx.closePath();
ctx.stroke();
}
}
function drawRadarMap(radarLayers, polygonPerStep, radarMapTotalSides, radarX, radarY, ctx) {
let axis = []; // 用于保存每一个多边形的每一个点坐标
// ...
for (let j = 0; j < radarLayers; j++) { // 绘制 radarLayers 层的雷达图
drawPolygon(radarMapTotalSides, j, radius, radarX, radarY, axis, ctx);
// ...
}
drawStria(radarMapTotalSides, axis, radarX, radarY, ctx);
}
axis[length - 1].coords代表雷达图中最外层的多边形的所有点坐标。顺时针遍历其中元素,ctx.lineTo(coords[i].x, coords[i].y)从圆点开始依次连接最外层的多边形的点,从而构成一条条直线。

绘制雷达图外层文本
每一条直线都代表一个量化的数据,跟柱状图一样,底部都有一个标题表示什么量化的数据:
/**
* @param data 顺时针开始最外层每一个点的文本;
* @param axios 最外层多边形每一个点的坐标信息;
* @param currentPoint 当前循环到的多边形的一个点坐标;
* @param radarX 雷达图中心坐标的 x 坐标轴。
*/
function drawPointText(axis, currentPoint, radarX, ctx, data) {
ctx.font = `16px Georgia`;
if (axis[i].x <= radarX) {
ctx.textAlign = "right";
} else {
ctx.textAlign = "left";
}
ctx.fillText(data[currentPoint].title, axis[currentPoint].x, axis[currentPoint].y);
}
从这里开始涉及到数据了,这里开始提供数组:
点击查看代码
let dataArea = [
{
title: "js",
star: 4
},
{
title: "ts",
star: 2
},
{
title: "html",
star: 4
},
{
title: "css",
star: 4
},
{
title: "vue",
star: 4
},
{
title: "uniapp",
star: 4
},
{
title: "java",
star: 2
},
{
title: "flutter",
star: 3
},
{
title: "dart",
star: 4
},
{
title: "python",
star: 0
}
];
drawPointText()也是需要我们前面保存的交界点的坐标信息,而文本只需要存在于最外层的坐标信息。这个函数应该放在drawStria()函数内的 for 循环里面:
function drawStria(radarMapTotalSides, axis, radarX, radarY, ctx, data) {
let coords = axis[axis.length - 1].coords;
for (let i = 0; i < radarMapTotalSides; i++) {
// ...
drawPointText(coords, coords[i], radarX, radarY, ctx, data);
}
}
注意:从这里开始,函数又需要新增 data 参数,从最上层往下依次传递这个 data,请自行添加。

绘制数据区域
每一条直线与最外层的交界点相连的点,我称之为数据点。这些数据点又相连构成一个闭合的数据区域,再填充一些颜色,这一项工作完成雷达图就完整了。
function calcDataAreaTopX(areaTopLayer, axis, radarX, currentPoint) {
if (areaTopLayer < 0) {
return radarX;
} else {
return axis[areaTopLayer].coords[currentPoint].x;
}
}
function calcDataAreaTopY(areaTopLayer, axis, radarY, currentPoint) {
if (areaTopLayer < 0) {
return radarY;
} else {
return axis[areaTopLayer].coords[currentPoint].y;
}
}
function drawDataAreaTop(axis, currentPoint, radarX, radarY, ctx, data) {
let x = calcDataAreaTopX(data[currentPoint].star - 1, axis, radarX, currentPoint);
let y = calcDataAreaTopY(data[currentPoint].star - 1, axis, radarY, currentPoint);
if (currentPoint === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
return { x: x, y: y };
}
function drawDataArea(radarMapTotalSides, axis, radarX, radarY, ctx, data) {
let areaTopAxis = []; // 数据区域的所有点坐标
ctx.beginPath();
for (let i = 0; i < radarMapTotalSides; i++) {
let { x, y } = drawDataAreaTop(axis, i, radarX, radarY, ctx, data);
areaTopAxis.push({ title: data[i].title, star: data[i].star, x: x, y: y });
}
ctx.closePath();
ctx.strokeStyle = "rgba(68,226,155, 1)";
ctx.stroke();
ctx.fillStyle = "rgba(81,182,137, 0.6)";
ctx.fill();
return areaTopAxis;
}
这里需要特别说明data[currentPoint].star - 1,因为 star 是从 0 开始,最大值为 5,必须要减 1,不然数组索引值越界。drawDataArea()将返回数据区域的顶点坐标。drawDataArea有以下五个参数:
- axis:顺时针开始最外层每一个点的文本;
- currentPoint:当前循环到的多边形的一个点坐标;
- radarX:雷达图中心坐标的 x 坐标轴;
- radarY:雷达图中心坐标的 y 坐标轴。
- data: 数据源
function drawRadarMap(radarLayers, polygonPerStep, radarMapTotalSides, radarX, radarY, ctx, data) {
// ...
// ...
let topCoords = drawDataArea(radarMapTotalSides, axis, radarX, radarY, ctx, data);
}

雷达图浮动面板
本小节是对雷达图的一个扩展内容,实现鼠标悬浮在数据点至上时可以显示详细信息的功能:
<div id="radar-wrap">
<canvas id="radar-map" width="400" height="400">Your browser version is too late.</canvas>
<div id="floating-panel"></div>
</div>
点击查看 CSS 代码
#radar-map {
cursor: pointer;
position: absolute;
border: 1px solid rgba(110, 110, 110, 0.8);
border-radius: 10px;
}
#radar-wrap {
width: 400px;
height: 400px;
box-sizing: border-box;
position: relative;
}
#floating-panel {
position: absolute;
display: none;
border-style: solid;
white-space: nowrap;
z-index: 9999999;
transition: left 0.4s cubic-bezier(0.23, 1, 0.32, 1) 0s, top 0.4s cubic-bezier(0.23, 1, 0.32, 1) 0s;
background-color: rgba(50, 50, 50, 0.7);
border-width: 0;
border-color: rgb(51, 51, 51);
border-radius: 4px;
color: rgb(255, 255, 255);
font: 14px / 21px "Microsoft YaHei";
padding: 5px;
left: 29px;
top: 145px;
pointer-events: none;
}
鼠标停留在数据区域的顶点时,出现该点的详细信息,而顶点的坐标信息由drawDataArea函数提供。在下面的这个函数中,只需要遍历坐标信息是否接近于我们鼠标的 offsetX 和 offsetY 区域就显示详细信息:
function drawFloatingPanel(axis) {
let floatingPanel = $("#floating-panel");
let timeout = null;
$("#radar-map").on({
mousemove: function (e) {
if (timeout != null) clearTimeout(timeout);
timeout = setTimeout(() => {
axis.forEach((value, index) => {
if (value.x >= e.offsetX - 5 && value.x < e.offsetX + 5 && value.y >= e.offsetY - 5 && value.y < e.offsetY + 5) {
$(floatingPanel).css({
display: "block",
left: `${e.offsetX}px`,
top: `${e.offsetY}px`
});
$(floatingPanel).empty().append(`
<div class="tech">技术:${value.title}</div>
<div class="star">掌握程度:${value.star} 颗星</div>
`);
}
});
}, 50);
},
mouseleave: function (e) {
$(floatingPanel).css({ display: "none" });
}
});
}
GitHub 仓库
01#Web 实战:雷达图的更多相关文章
- [2014.01.27]wfRadar 雷达图组件 2.5
全新开发的雷达图组件--wfRadar,使用简单,功能强大,图像处理效果极佳. 组件支持多种图片格式,包括bmp,jpg,gif,wmf,emf,ico,png,pcx,tif,tga,pcx,dcx ...
- Web 前端实战(三):雷达图
前言 在<Canvas 线性图形(五):多边形>实现了绘制多边形的函数.本篇文章将记录如何绘制雷达图.最终实现的效果是这样的: 绘制雷达图 雷达图里外层 如动图中所示,雷达图从里到外一共有 ...
- Web 前端实战:雷达图
前言 在Canvas 线性图形(五):多边形实现了绘制多边形的函数.本篇文章将记录如何绘制雷达图.最终实现的效果是这样的: 绘制雷达图 雷达图里外层 如动图中所示,雷达图从里到外一共有 6 层,所以, ...
- HTML5 Canvas制作雷达图实战
雷达图又叫蜘蛛网图,是一种对各项数据查看很明显的表现图,在很多游戏中,对游戏中的每个角色的分析图一般也用这种图. 下面,用HTML5的Cavas来实现雷达图. 效果 一.创建Canvas var mW ...
- asp.net comp雷达图
<system.web> <httpHandlers> <add path="ChartImg.axd" verb="GET,HEAD,PO ...
- konva canvas插件写雷达图示例
最近,做了一个HTML5的项目,里面涉及到了雷达图效果,这里,我将react实战项目中,用到的雷达图单拎出来写一篇博客,供大家学习. 以下内容涉及的代码在我的gitlab仓库中:Konva canva ...
- 数据可视化基础专题(十二):Matplotlib 基础(四)常用图表(二)气泡图、堆叠图、雷达图、饼图、
1 气泡图 气泡图和上面的散点图非常类似,只是点的大小不一样,而且是通过参数 s 来进行控制的,多的不说,还是看个示例: 例子一: import matplotlib.pyplot as plt im ...
- Python绘制雷达图(俗称六芒星)
原文链接:https://blog.csdn.net/Just_youHG/article/details/83904618 背景 <Python数据分析与挖掘实战> 案例2–航空公司客户 ...
- 帆软报表(finereport)雷达图钻取详细点新页面展示
添加参数栏,季度下拉框的空间名为combobox0 添加雷达图,通过第三页面做跳转 雷达图钻取.cpt为联动钻取的第三页面 添加纬度(所点击钻取的点) 参数 wd 添加季度参数 jd 值为季 ...
- WPF DevExpress 设置雷达图Radar样式
DevExpress中定义的ChartControl很不错,很多项目直接使用这种控件. 本节讲述雷达图的样式设置 <Grid> <Grid.Resources> <D ...
随机推荐
- 从稍微懂一点开始的C++学习之路1: 智能指针
从稍微懂一点开始的C++学习之路1 智能指针 因为之前一直是搞qt的,没有搞过纯c++,所以现在算得上是刚开始学纯C++.C++的大部分语法其实我都懂,主要的是一些规范,还有内存回收等一些细节地方纯C ...
- 使用浏览器inspect调试app
使用浏览器inspect调试app 在开发混合项目的过程中,常常需要在app环境排查问题,接口可以使用fiddler等工具来抓包,但是js错误就不好抓包了,这里介绍一种调试工具-浏览器. 1.调试过程 ...
- 正则提取器和beanshell处理器组合,将提取的所有id拼接成字符串
1.添加正则表达式,提取所有id值 2.添加beanshell处理器将所有的id值拼接成字符串 方法一: int N = Integer.parseInt(vars.get("build_m ...
- 分享一个自己项目中用到的.net中正则替换工具处理类(支持先用特征匹配内容整体模板,同时模板内对相关字内容进行替换)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Redis set数据类型命令使用及应用场景使用总结
转载请注明出处: 目录 1.sadd 集合添加元素 2.srem移除元素 3.smembers 获取key的所有元素 4.scard 获取key的个数 5.sismember 判断member元素是否 ...
- .NET周报【12月第4期 2022-12-31】
祝大家新年快乐! 国内文章 『 再看.NET7』数值类型 https://mp.weixin.qq.com/s/ctiBMPY6Hditk81AzHSRng 在C#中,有int16,用short来定义 ...
- 多项式 I:拉格朗日插值与快速傅里叶变换
1. 复数和单位根 前置知识:弧度制,三角函数. 1.1 复数的引入 跳出实数域 \(\mathbb R\),我们定义 \(i ^ 2 = -1\),即 \(i = \sqrt {-1}\),并在此基 ...
- linux系统一键开启root登陆
服务器只能key登陆,用这个后直接可以root方式登陆 sudo -i echo root:要设置的密码 |sudo chpasswd root sudo sed -i 's/^#\?PermitRo ...
- AspNetCore底层源码剖析(三)IOC
title: AspNetCore底层源码剖析(三)IOC date: 2022-09-21 13:20:01 categories: 后端 tags: - .NET 介绍 每个 ASP.NET Co ...
- 【白话模电1】PN结与二极管
距离上一次写半导体,已经过了很久了,上次分享了本征半导体的基本概念: https://zhuanlan.zhihu.com/p/109483580 今天给大家聊聊半导体工业中的基础:PN结与二极管 1 ...