canvas绘制雷达图
最近做的一个需求,场景之一是绘制一个雷达图,找了一圈,似乎 AntV 下的 F2 很适合拿来主义:
但是接着又考虑了一下,我当前所做的项目并不是可视化项目,今后大概率也不会有这种可视化图表的需求,只是为了单个需求一两个图表就引入一个可视化库,性价比有点低,辛辛苦苦优化下来的代码体积,就因为一个冗余的代码库一下子回到解放前,那可真要不得(虽然 F2已经够精简的了)
再加上我刚好对于 canvas
这块有点兴趣,送上门的练手机会更不可能错过了,除此之外,我看了一下 F2的文档 看得有点脑壳疼,有看这文档的时间我还不如直接去看 Canvas
原生 Api
呢,于是决定自己来搞定这个东西
绘制多边形
这个雷达图看起来好像挺简单,实际上还是有点门道的,我考虑了一下,将其分成三部分:
- 正多边形
- 正多边形顶点处文案
- 雷达区域
多边形示意如下(这里以正五边形作为示例):
这个图其实由多个尺寸不同的正五边形嵌套而成,并且通过 5
条线将正五边形的对角顶点连在了一起,关键在于需要知道正五边形的五个顶点坐标,这其实就是求解几何数学题
如图,旋转正五边形,令其中心点位于坐标轴原点,其最左侧的一条边平行于 y
轴,在此状态下的其 x
坐标最大的点(即最右侧的点)位于 x
轴上,然后再画一个此正五边形的外接圆,接下来就可以进行求解了
这里之所以这么旋转正五边形,只是为了更方便的求解坐标,你当然也可以令正五边形的一条边平行于 x
轴或者其他任意的旋转进行求解,只要能取得正多边形各个顶点的相对坐标即可
显而易见神特么显而易见,正五边形的每个顶点的坐标就是:
(radius * cosθ, radius * sinθ)
这里的 radius
就是正五边形外接圆半径,θ
是顶点与原点之间连线和 x
的夹角
其中 radius
是我们自己规定的,只剩下 θ
的求解了,按照上图,如果正多边形最右侧(即 x
坐标最大)的顶点为第一个顶点,逆时针旋转依次为 第二、第三…第 n
显而易见的是,这个 θ
值其实就是正五边形内角角度的一半,正多边形(n
)内角角度(mAngle
)为 Math.PI * 2 / n
, 则第 n
个点的坐标为:
(radius * cos(θ * (n - 1)), radius * sin(θ * (n - 1)))
这里只是拿正五边形举个例子,放宽到正n
边形都是这个道理
顶点拿到了,正多边形就很好画了,不过还有一点需要注意,实际需求中,一般是要求正多边形是正着放置,即底边与 x
平行,而按照本文这里的求解方式得到的顶点坐标画出来的正多边形,是侧边与 y
平行,所以需要将得到的 正多边形的坐标进行一定的映射,将之转换为正着放置的
canvas
也提供了这种操作,即 rotate
,只要先把或者后把 canvas
的坐标系旋转一下,那么画出来的多边形在视觉上看就是 正着放置的了
function drawPolygon () {// #region 绘制多边形const r = mRadius / polygonCountlet currentRadius = 0for (let i = 0; i < polygonCount; i++) {bgCtx.beginPath()currentRadius = r * (i + 1)for (let j = 0; j < mCount; j++) {const x = currentRadius * Math.cos(mAngle * j)const y = currentRadius * Math.sin(mAngle * j)// 记录最外层多边形各个顶点的坐标if (i === polygonCount - 1) {polygonPoints.push([x, y])}j === 0 ? bgCtx.moveTo(x, y) : bgCtx.lineTo(x, y)}bgCtx.closePath()bgCtx.stroke()}// #endregion// #region 绘制多边形对角连线for (let i = 0; i < polygonPoints.length; i++) {bgCtx.moveTo(0, 0)bgCtx.lineTo(polygonPoints[i][0], polygonPoints[i][1])}bgCtx.stroke()// #endregion
}
正多边形顶点处文案
文案的位置其实就是在顶点附近,按照顶点的坐标进行一定的偏移即可,但前面说过了,由于 canvas
坐标系已经通过 rotate
进行了旋转,这里想要让绘制出来的文字是正着放置的,就需要再次将坐标系旋转回去
除此之外,还要注意一下文字绘制的对其方式,这个通过 textAlign
可以解决:
function drawVertexTxt () {bgCtx.font = 'normal normal lighter 16px Arial'bgCtx.fillStyle = '#333'// 奇数多边形,距离设备顶边最近的点(即最高点的那一点),需要专门设置一下 textAlignconst topPointIndex = mCount - Math.round(mCount / 4)for (let i = 0; i < polygonPoints.length; i++) {bgCtx.save()bgCtx.translate(polygonPoints[i][0], polygonPoints[i][1])bgCtx.rotate(rotateAngle)let indentX = 0let indentY = 0if (i === topPointIndex) {// 最高点bgCtx.textAlign = 'center'indentY = -8} else {if (polygonPoints[i][0] > 0 && polygonPoints[i][1] >= 0) {bgCtx.textAlign = 'start'indentX = 10} else if (polygonPoints[i][0] < 0) {bgCtx.textAlign = 'end'indentX = -10}}// 如果是正四边形,则需要单独处理最低点if (mCount === 4 && i === 1) {bgCtx.textAlign = 'center'indentY = 10}// 开始绘制文案mData[i].titleList.forEach((item, index) => {bgCtx.fillText(item, indentX, indentY + index * 20)})bgCtx.restore()}
}
雷达区域
雷达区域就是文章开头那个图中的红色线框内的区域,这个区域也是一个多边形,只不过不是正的,但坐标的求解其实和正多边形是差不多的,只需要在求坐标的过程中,对坐标参数进行一定比例的缩放罢了,而这个比例就是所在的顶点代表的实际值与总值的比例(比如,100分是满分,第一个点只有80分,那么就是 80%
)
如果只是一个静态图,那么到此为止也就没什么好说的了,求得雷达区域各个点坐标,然后连接路径、闭合路径,再描边就完事,但如果是想要做成文章开头那种雷达区域动态填充的,就稍微要麻烦一点了
我一开始的想法是,动态求解每一帧雷达区域的各个顶点坐标,后来算了半天发现也太麻烦了,怎么扯出来那么多数学公式,这就算是能求出来性能应该也好不到哪里去吧
遂弃之,另寻他法,忽得一技
文章开头的那种动态填充法,看起来很像是一个圆以扇形打开的样子啊,看了一下 canvas
里有个叫 clip
的东西,于是想到,只要先将需要的雷达区域裁切(clip
)好,再用一个足够覆盖这个裁切区域的圆放到这个裁切面上进行动态扇形展开,不就达到目的了吗?
for (let i = 0; i < mCount; i++) {// score不能超过 fullScorescore = Math.min(mData[i].score, mData[i].fullScore)const x = Math.cos(mAngle * i) * score / mData[i].fullScoreconst y = Math.sin(mAngle * i) * score / mData[i].fullScorei === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y)
}
ctx.closePath()
ctx.clip()
// ...
ctx.moveTo(0, 0)
ctx.arc(0, 0, canvasMaxSize, 0, currentAngle)
ctx.closePath()
ctx.fill()
效果如图:
似乎可行,但是与文章开头的那个图对比了一下,发现还有点欠缺,头图的雷达区域是有红色描边的,并且在完整绘制完毕后,雷达区域的每个顶点处都有红色小圆点
顶点处的红色小圆点好办,顶点坐标是已知的,无非是在顶点处在画个小圆罢了,但是描边有点麻烦
描边的长度是紧跟雷达区域绘制进度的,这就需要知道每一帧雷达区域每个顶点的坐标,这不又回去了吗?说好了不搞那么多公式计算的
后来又想了下,如果事先把雷达图最终态画好,然后用一个蒙层遮住,接着再把这个蒙层动态打开不也行吗?
又看了一下canvas
的文档,发现了一个叫 globalCompositeOperation
的 API
,就是它了
为了方便绘制,我重新划分了一下,一共用了三个 canvas
第一个 canvas
作为最终呈现效果的画布,第二个用于绘制完整版的静态雷达区域,第三个则绘制用于给完整版的静态雷达区域进行遮罩的蒙层,三个canvas
一组合,就达到了预期效果:
小结
本文完整示例 Live Demo 和 示例代码 已经上传,感兴趣的可以亲自试下
canvas绘制雷达图相关推荐
- 利用Canvas绘制雷达图
雷达图(蜘蛛网图)是一种常见的数据分析图表,本文采用canvas来绘制雷达图,并最终封装成一个小组件.首先来看一下最终的效果图: 如何画正多边形 以正五边形雷达图为例(其他任意正多边形也一样),如下图 ...
- html5雷达图绘制,Canvas 绘制雷达图
最近做的一个需求,场景之一是绘制一个雷达图,找了一圈,似乎 AntV 下的 F2 很适合拿来主义: 但是接着又考虑了一下,我当前所做的项目并不是可视化项目,今后大概率也不会有这种可视化图表的需求,只是 ...
- 带着canvas去流浪系列之六 绘制雷达图
[摘要] 用canvas原生API实现百度Echarts基本图表. 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvas ...
- 【带着canvas去流浪(6)】绘制雷达图
目录 一. 任务说明 二. 重点提示 三. 示例代码 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文 ...
- echarts 雷达图_【带着canvas去流浪】绘制雷达图
使用原生canvasAPI绘制雷达图.(截图以及数据来自于百度Echarts官方示例库[查看示例链接]). 二. 重点提示 雷达图绘制的看起来并不复杂,无非就是一些路径点的连线,其中的难点都在于一些细 ...
- android 雷达坐标系,Android Path之绘制雷达图的技巧
Android Path之绘制雷达图的技巧,绘制蜘蛛网络其实就是绘制指定边数的正多边形,这一步比较简单,比较难的可能就是每个顶点的算法,相关注释我都写了,还有一张来自互联网的图以助于思考,如下: 第一 ...
- python雷达图数据_PYTHON绘制雷达图代码实例
这篇文章主要介绍了PYTHON绘制雷达图代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.雷达图 import matplotlib.py ...
- python【Matlibplot绘图库】利用matlibplot绘制雷达图
文章目录 1.基本构造 2.比较功能 1.基本构造 之前在一些数据分析案例中看到用 Go 语言绘制的雷达图,非常的漂亮,就想着用matlibplot.pyplot也照着画一个,遗憾的是matlibpl ...
- 圆形和多边形雷达图python-Matplotlib绘制雷达图和三维图的示例代码
1.雷达图 程序示例 '''1.空白极坐标图''' import matplotlib.pyplot as plt plt.polar() plt.show() '''2.绘制一个极坐标点''' im ...
最新文章
- Tensorrt一些优化技术介绍
- 没有头文件调用cpp_VS2017中同一个解决方案下不同工程的调用
- HDU-5532(LIS-nlogn)
- C++学习笔记:(四)运算符重载 类型转换
- hdu 2196 树形dp
- Delphi 7自带的TeeChart组件
- 主板找不到SSD解决一例
- C语言的全局变量和局部变量的作用域
- curl的php多线程类,php利用curl实现多线程类的示例
- php mysql含引号报错,执行sql双引号
- 【Vue3 + SpringBoot】搭建企业日报管理saas系统
- 国二计算机考试office快捷键,全国计算机二级考试(Office)应试技巧
- 太可爱啦!程序员把电脑病毒当宠物养
- 全球程序员收入出炉!北京收入排入全球第十
- linux notepadqq不支持中文输入的原因分析
- Docker最新版19.03 详细教程
- 化妆品用植物干细胞的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- 虹科案例 | 解决ASRS系统的痛点问题居然这么简单?(下)
- SEA创建、网卡聚合
- 用于实时视频监控的摇摄/倾斜/变焦摄像机中具有复杂背景的鲁棒运动检测
热门文章
- python图形化编程更改内部参数_构建FunctionTrace,一个图形化的Python分析器
- [计蒜客T2238]礼物_线段树_归并排序_概率期望
- 四、buildroot中添加自己的软件包
- beego安装bee工具时出现unable to access ‘https://github.com/xxxx/xxxx‘: Failed to connect togo....解决办法
- wxpython 介绍
- FileZilla Server.xml 如何配置
- 【IOS问题解决方法】 Showing All Messages :-1: Undefined symbol: _OBJC_CLASS_$_CMMotionManager
- 支付系统项目简介与资源介绍
- 2017年11月学习心得报告
- 1 回忆第一次写博客的故事