Web 前端实战:雷达图
前言
在Canvas 线性图形(五):多边形实现了绘制多边形的函数。本篇文章将记录如何绘制雷达图。最终实现的效果是这样的:
绘制雷达图
雷达图里外层
如动图中所示,雷达图从里到外一共有 6 层,所以,我们需要改造绘制多边形的函数:
点击查看绘制基础雷达图代码
function calcPolygonX(radarX, radius, increaseAngle) {
return radarX + radius * Math.cos(increaseAngle);
}
function calcPolygonY(radarY, radius, increaseAngle) {
return radarY - radius * Math.sin(increaseAngle);
}
// 绘制多边形的函数
function drawPolygon(radarMapTotalSides, radius, radarX, radarY, ctx) {
let averageAngle = Math.PI * 2 / sides;
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();
}
// 绘制雷达图的函数
function drawRadarMap(radarLayers, polygonPerStep, radarMapTotalSides, radarX, radarY, ctx) {
let radius = polygonPerStep;
for ( let j = 0; j < radarLayers; j++ ) {
drawPolygon(radarMapTotalSides, radius, radarX, radarY, ctx);
radius = radius + polygonPerStep;
}
}
polygonPerStep
的意思是每一个多边形之间相差多少距离,radarMapTotalSides
的意思是雷达图的多边形是几边形:
let canvas = document.getElementById("radar-map");
let ctx = canvas.getContext("2d");
drawRadarMap(5, 40, 10, 300, 300, ctx);
到目前为止我们就利用绘制多边形的函数drawPolygon
成功绘制了一个雷达图的雏形:
当前的雷达图还缺少了最外层每一个点的文本,以及连接雷达图中心到最外层点的直线。
连接雷达图里外层
连接雷达图里外层时,要在绘制多边形的时候保存每一层每一个点的坐标,也就是在drawPolygon
函数中保存坐标信息。
保存点坐标
在drawRadarMap
函数里面声明一个数组axis
数组,它专门用于保存每层多边形的每个点坐标信息:
function drawRadarMap(radarLayers, polygonPerStep, radarMapTotalSides, radarX, radarY, ctx) {
let axis = []; // 用于保存每一个多边形的每一个点坐标
let radius = polygonPerStep;
for ( let j = 0; j < radarLayers; j++ ) {
drawPolygon(radarMapTotalSides, radius, radarX, radarY, axis, j, ctx);
radius = radius + polygonPerStep;
}
}
从顺时针开始的第一个点的坐标到最后一个点坐标,以及这些点坐标在第几层多边形上:
function drawPolygon(radarMapTotalSides, radius, radarX, radarY, axis, currentPolygonLayer, ctx) {
let averageAngle = Math.PI * 2 / sides;
let increaseAngle = 0;
let targetX, targetY;
ctx.beginPath();
axis.push({ layer: currentPolygonLayer, coords: [] }); // 保存点坐标的数组,
for ( let i = 0; i < radarMapTotalSides; i++ ) {
targetX = calcPolygonX(radarX, radius, increaseAngle);
targetY = calcPolygonY(radarY, radius, increaseAngle);
ctx.lineTo(targetX, targetY);
increaseAngle += averageAngle;
axis[currentPolygonLayer].coords.push({ x: targetX, y: targetY }); // 添加点坐标到数组中
}
ctx.closePath();
ctx.stroke();
}
drawPolygon
函数新增了两个参数:axis 和 currentPolygonLayer。axis 就是保存没层多边形点的坐标数组;currentPolygonLayer 就是当前多边形在第几层多边形,比如第一层就是 0。
绘制直线函数
上面的工作是点坐标,目的就是连接雷达图中心点到最外层多边形的每一个点。所以,在这里我们新增一个函数,这个函数专门处理连接直线的函数drawStria
:
function drawStria(radarLayers, axis, radarX, radarY, ctx) {
let coords = axis[axis.length - 1].coords;
for ( let i = 0; i < radarLayers; i++ ) {
ctx.beginPath();
ctx.moveTo(radarX, radarY);
ctx.lineTo(coords[i].x, coords[i].y);
ctx.closePath();
ctx.stroke();
drawPointText(data, coords, i, radarX, ctx);
}
}
axis[length - 1].coords
代表雷达图中最外层的多边形的所有点坐标。顺时针遍历其中元素,ctx.lineTo(coords[i].x, coords[i].y)
从圆点开始依次连接最外层的多边形的点,从而构成一条条直线。
绘制雷达图外层文本
在雷达图最外层的多边形的每一个点添加文本,表示直线代表的是什么数据。
function drawPointText(data, axis, currentPoint, radarX, ctx) {
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);
}
对于这个函数的几个参数讲解:
- data:顺时针开始最外层每一个点的文本;
- axios:最外层多边形每一个点的坐标信息;
- currentPoint:当前循环到的多边形的一个点坐标;
- radarX:雷达图中心坐标的 x 坐标轴。
在drawStria
函数循环体内调用该函数完成最外层的文本渲染:
绘制数据区域
接下来就是雷达图最重要的部分了。雷达图中每一条直线上该文本所达到的值在此直线上进行移动,连接这些点构成一块区域,就是数据区域。
点击查看绘制数据区域的完整代码
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) {
let x = calcDataAreaTopX(data[currentPoint].star - 1, axis, radarX, currentPoint);
let y = calcDataAreaTopY(data[currentPoint].star - 1, axis, radarY, currentPoint);
if ( i === 0 ) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
return { x: x, y: y };
}
function drawDataArea(radarMapTotalSides, radius, axis, radarX, radarY, data, ctx) {
ctx.beginPath();
for ( let i = 0; i < radarMapTotalSides; i++ ) {
drawDataAreaTop(axis, i, radarX, radarY, ctx);
}
ctx.closePath();
ctx.strokeStyle = "rgba(68,226,155, 1)";
ctx.stroke();
ctx.fillStyle = "rgba(81,182,137, 0.6)";
ctx.fill();
}
需要为数据函数提供一个data
,这个是数据区域的信息,最外层多边形每一个点对应的值:
点击查看数据区域的数据
let data = [
{
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
}
];
以上就是绘制数据区域的函数的完整代码。drawDataAreaTop
是圈画数据区域的函数,而drawDataArea
完成最后的颜色填充工作。drawDataAreaTop
接收以下几个参数:
- axis:顺时针开始最外层每一个点的文本;
- currentPoint:当前循环到的多边形的一个点坐标;
- radarX:雷达图中心坐标的 x 坐标轴;
- radarY:雷达图中心坐标的 y 坐标轴。
这里需要特别说明data[currentPoint].star - 1
,因为 star 是从 0 开始,最大值为 5,必须要减 1,不然数组索引值越界。
调用雷达图函数
在绘制雷达图的函数下面添加drawStria
和drawDataArea
两个函数,完整一个完整的雷达图绘制
function drawRadarMap(radarLayers, polygonPerStep, radarMapTotalSides, radarX, radarY, ctx) {
// ...
// ...
drawStria(radarMapTotalSides, axis, radarX, radarY, ctx);
drawDataArea(radarMapTotalSides, axis, radarX, radarY, data, ctx);
}
drawRadarMap(5, 40, 10, 300, 300, ctx);
雷达图浮动面板
这一节是扩展雷达图的功能,当鼠标浮在数据区域节点之上时,就出现一个浮动面板,展示具体的数据信息。浮动面板和雷达图被包裹在一个 div 标签里,并设置为相对定位,浮动面板设置为绝对定位。
<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;
}
接下来就是最重要的 JS 代码,这里为了方便控制样式,我就用 JQuery 来实现。这里需要改造一下drawDataArea
函数,我们要保存数据区域每一个点的坐标。
点击查看 JQuery 代码
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" });
}
});
};
Gitee 仓库
https://gitee.com/shiramashiro/web-learning/tree/main/src/03.案例
Web 前端实战:雷达图的更多相关文章
- Web 前端实战:Gitee 贡献图
前言 这次要做的 Web 前端实战是一个 Gitee 个人主页下的贡献图(在线 Demo),偶尔做一两个,熟悉熟悉 JS 以及 jQ.整体来说这个案例并不难,主要是控制第一个节点以及最后一个节点处于星 ...
- Web 前端实战(三):雷达图
前言 在<Canvas 线性图形(五):多边形>实现了绘制多边形的函数.本篇文章将记录如何绘制雷达图.最终实现的效果是这样的: 绘制雷达图 雷达图里外层 如动图中所示,雷达图从里到外一共有 ...
- 20151028整理罗列某种开发所包括对技术(技术栈),“较为全面”地表述各种技术大系的图表:系统开发技术栈图、Web前端技术栈图、数据库技术栈图、.NET技术栈图
———————————— 我的软件开发生涯 (10年开发经验总结和爆栈人生) 爆栈人生 现在流行说全栈.每种开发都有其相关的技术.您是否觉得难以罗列某种开发所包括对技术(技术栈)呢? 您是否想过: ...
- Web 前端实战:JQ 实现树形控件
前言 这是一篇个人练习 Web 前端各种常见的控件.组件的实战系列文章.本篇文章将介绍个人通过 JQuery + 无序列表 + CSS 动画完成一个简易的树形控件. 最终实现的效果是: 这样结构比较复 ...
- 如何在Web前端实现CAD图文字全文搜索功能之技术分享
现状 在CAD看图过程中我们经常会需要用到查找文字的功能,在AutoCAD软件查找一个文字时,可以通过打开左下角输入命令find,输入查找的文字,然后设置查找范围,就可以搜索到需要查询的文字.但在We ...
- 【h5+c3】web前端实战项目、快装webapp手机案例源码
快装WebApp项目(Web移动端开发案例)webapp移动端项目源码.html5+css3实战案例分享.微信端H5实例开发 简介快装WebApp是一个面向移动端的快速装修app,此项目为手机端:使用 ...
- Web 前端实战:JQ 实现下拉菜单
实现过程 实现一个简易的鼠标悬停菜单项显示其子项的下拉框控件.将用到 CSS 绝对定位.流式布局.动画等:JQuery 鼠标移入和移出事件.DOM 查找.效果图如下: HTML 结构: <div ...
- 一张图告诉你移动Web前端所有技术(工程化、预编译、自动化)
你要的移动web前端都在这里! 大前端方向:移动Web前端.Native客户端.Node.js. 大前端框架:React.Vue.js.Koa 跨终端技术:HTML5.CSS 3.JavaScript ...
- web前端开发初学者必看的学习路线(附思维导图)
很多同学想学习WEB前端开发,虽然互联网有很多的教程.网站.书籍,可是却又不知从何开始如何选取.看完网友高等游民白乌鸦无私分享的原标题为<写给同事的前端学习路线>这篇文章,相信你会有所收获 ...
随机推荐
- Go微服务框架go-kratos实战04:kratos中服务注册和服务发现的使用
一.简介 关于服务注册和服务发现介绍,我前面的文章有介绍过 - 服务注册和发现的文章. 作为服务中心的软件有很多,比如 etcd,consul,nacos,zookeeper 等都可以作为服务中心. ...
- 30款提升组织效能 SaaS 工具,我们的宝藏工具箱大公开
熟悉 Juicedata 的小伙伴知道,从2017年成立到第一款产品发布.从寻找PMF(Product Market Fit) 到开源,我们一直保持着一个精简的团队配置,不少人都很好奇我们是如何做到的 ...
- AtCoder ABC 242 题解
AtCoder ABC 242 题解 A T-shirt 排名前 \(A\) 可得 T-shirt 排名 \([A+1,B]\) 中随机选 \(C\) 个得 T-shirt 给出排名 \(X\) ,求 ...
- python:selenium测试登录在chrome中闪退
问题描述:使用selenium.webdriver时测试网页,进行自动登录测试总是在登录成功时闪退.使用指定驱动器位置的方式chrome也会闪退 1.正常使用chrome驱动打开一个网页,正常访问 f ...
- flowable与camunda性能测试对比分析
前言 目前主流的Java开源流程引擎有Activiti.Flowable.Camunda,笔者在进行流程引擎技术选型时,除了功能方面,性能和稳定性是尤其关注的指标,是选择Flowable?还是Camu ...
- .NET中测试代码运行时间
更新记录 本文迁移自Panda666原博客,原发布时间:2021年6月29日. 计算代码运行的时间,除了呆萌地用秒表去计时,或者可以通过Visual Studio来查看,还可以在.NET代码中使用St ...
- ebook下载 | 灵雀云发布《 企业高管IT战略指南——为何选择容器与Kubernetes》
发送关键词[高管指南]至灵雀云公众号,立即下载完整版电子书 "本书将提供企业领导者/IT高管应该了解的,所有关于容器技术和Kubernetes的基础认知和关键概念,突破技术语言屏障,全面梳理 ...
- 记一次学习kibaba踩过的坑(Windows环境)
下载地址 ElasticSearch:https://www.elastic.co/cn/downloads/elasticsearchLogstash:https://www.elastic.co/ ...
- 18.Tomcat部署及优化
Tomcat部署及优化 目录 Tomcat部署及优化 Tomcat简介 Tomcat核心组件 Web容器 什么是 servlet? 什么是 JSP? Container 结构分析 Tomcat 请求过 ...
- 3.对互斥事件和条件概率的相互理解《zobol的考研概率论教程》
tag:这篇文章没太多思考的地方,就是做个过渡 1.从条件概率来定义互斥和对立事件 2.互斥事件是独立事件吗? 3.每个样本点都可以看作是互斥事件,来重新看待条件概率 一.从条件概率来定义互斥和对立事 ...