成品演示

绘制雷达图

雷达图里外层

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有以下五个参数:

  1. axis:顺时针开始最外层每一个点的文本;
  2. currentPoint:当前循环到的多边形的一个点坐标;
  3. radarX:雷达图中心坐标的 x 坐标轴;
  4. radarY:雷达图中心坐标的 y 坐标轴。
  5. 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 仓库

  1. 可配置的雷达图
  2. 雷达图的基础代码
  3. 在线浏览雷达图

01#Web 实战:雷达图的更多相关文章

  1. [2014.01.27]wfRadar 雷达图组件 2.5

    全新开发的雷达图组件--wfRadar,使用简单,功能强大,图像处理效果极佳. 组件支持多种图片格式,包括bmp,jpg,gif,wmf,emf,ico,png,pcx,tif,tga,pcx,dcx ...

  2. Web 前端实战(三):雷达图

    前言 在<Canvas 线性图形(五):多边形>实现了绘制多边形的函数.本篇文章将记录如何绘制雷达图.最终实现的效果是这样的: 绘制雷达图 雷达图里外层 如动图中所示,雷达图从里到外一共有 ...

  3. Web 前端实战:雷达图

    前言 在Canvas 线性图形(五):多边形实现了绘制多边形的函数.本篇文章将记录如何绘制雷达图.最终实现的效果是这样的: 绘制雷达图 雷达图里外层 如动图中所示,雷达图从里到外一共有 6 层,所以, ...

  4. HTML5 Canvas制作雷达图实战

    雷达图又叫蜘蛛网图,是一种对各项数据查看很明显的表现图,在很多游戏中,对游戏中的每个角色的分析图一般也用这种图. 下面,用HTML5的Cavas来实现雷达图. 效果 一.创建Canvas var mW ...

  5. asp.net comp雷达图

    <system.web> <httpHandlers> <add path="ChartImg.axd" verb="GET,HEAD,PO ...

  6. konva canvas插件写雷达图示例

    最近,做了一个HTML5的项目,里面涉及到了雷达图效果,这里,我将react实战项目中,用到的雷达图单拎出来写一篇博客,供大家学习. 以下内容涉及的代码在我的gitlab仓库中:Konva canva ...

  7. 数据可视化基础专题(十二):Matplotlib 基础(四)常用图表(二)气泡图、堆叠图、雷达图、饼图、

    1 气泡图 气泡图和上面的散点图非常类似,只是点的大小不一样,而且是通过参数 s 来进行控制的,多的不说,还是看个示例: 例子一: import matplotlib.pyplot as plt im ...

  8. Python绘制雷达图(俗称六芒星)

    原文链接:https://blog.csdn.net/Just_youHG/article/details/83904618 背景 <Python数据分析与挖掘实战> 案例2–航空公司客户 ...

  9. 帆软报表(finereport)雷达图钻取详细点新页面展示

    添加参数栏,季度下拉框的空间名为combobox0 添加雷达图,通过第三页面做跳转 雷达图钻取.cpt为联动钻取的第三页面 添加纬度(所点击钻取的点) 参数   wd 添加季度参数 jd    值为季 ...

  10. WPF DevExpress 设置雷达图Radar样式

      DevExpress中定义的ChartControl很不错,很多项目直接使用这种控件. 本节讲述雷达图的样式设置 <Grid> <Grid.Resources> <D ...

随机推荐

  1. 【Java EE】Day11 BootStrap、响应式布局、栅格系统、CSS样式、案例

    一.BootStrap介绍 https://v3.bootcss.com/css/#overview 1.概念 基于三剑客开发的前端开发框架 定义了许多css样式和js插件,从而得到丰富的页面效果 依 ...

  2. [百度营]AI studio用法提醒(自用)

    持久化安装 需要设置持久化路径: !mkdir /home/aistudio/external-libraries !pip install beautifulsoup4 -t /home/aistu ...

  3. Go 快速入门指南 - 序言

    这本书是写什么的? 这是一本 Go 语言快速入门手册,目标读者是有任一编程语言基础,希望以最快的时间 (比如一个周末) 入门 Go 语言. 这本书应该怎么读? 书中几乎没有较长篇幅的理论知识,更多的是 ...

  4. JavaScript:是一种什么样的编程语言?

    有关JavaScript的发展历程,百度百科上已经说得很清楚了,这里不赘述,只是想谈一下我刚刚接触JS的一些感触. 作为后端java开发者,初次学习JS的时候,真的觉得JS非常的不严谨,很混乱.由于它 ...

  5. 后端流传输excel文件到前端

    场景 公司有个需求,请求接口返回一个对应的excel数据 方法 1.可以使用后端生成excel后,返回一个下载地址 2.可以把数据吐给前端,前端使用对应的插件转换成excel数据 3.使用流式传输 优 ...

  6. [编程基础] Python随机数生成模块总结

    Python随机数生成模块教程演示如何在Python中生成伪随机数. 1 介绍 1.1 随机数字生成器 随机数生成器(RNG)生成一组在外观上不显示任何可区分模式的值.随机数生成器分为两类:硬件随机数 ...

  7. 时钟同步服务器ntp安装文档

    应用场景 同步时钟很有必要,如果服务器的时间差过大会出现不必要的问题 大数据产生与处理系统是各种计算设备集群的,计算设备将统一.同步的标准时间用于记录各种事件发生时序, 如E-MAIL信息.文件创建和 ...

  8. dp 优化

    dp 优化 \(\text{By DaiRuiChen007}\) I. [ARC085D] - NRE \(\text{Link}\) 思路分析 将最终的第 \(i\) 对 \(a_i\) 和 \( ...

  9. C 语言初学者必备开发工具——Dev-Cpp [ 图文安装教程 ]

    前言 C 语言是一门功能强大的专业化编程语言,深受专业程序员和业余编程爱好者的喜爱,同时 C 语言也是当今最流行的嵌入式开发语言.大多数嵌入式项目的开发都是用 C 语言来编写的. 既然 C 语言这么厉 ...

  10. day04-Spring管理Bean-IOC-02

    Spring管理Bean-IOC-02 2.基于XML配置bean 2.7通过util空间名称创建list BookStore.java: package com.li.bean; import ja ...