konva canvas插件写雷达图示例
最近,做了一个HTML5的项目,里面涉及到了雷达图效果,这里,我将react实战项目中,用到的雷达图单拎出来写一篇博客,供大家学习。
以下内容涉及的代码在我的gitlab仓库中:
Konva canvas雷达图示例
仓库中有原生js实现的代码以及konva实现的代码!
先看效果图:
1. konva简单了解
现在js社区非常发达,有很多强大的插件,可以简化开发者的工作,我这里选用的canvas 2d插件是konva,它机会可以绘制我们能想到的所有平面图形,学习参考地址:
https://konvajs.org/docs/
这里我们简单了解下konva是如何工作的:
- konva的一起工作开始于Konva.stage, 它可以包含一个或者多个 Konva.Layer.
- 每一个 Konva.Layer 都有两个canvas渲染出来,一个画布用户显示,一个隐藏画布用于高性能事件监测
- 每一个 layer可以包含 shapes, groups
- groups可以包含 groups以及shapes
- stage, layers, groups, shapes都是 vitual nodes,类似于html页面的DOM nodes
- 所有的nodes都能够被设置style以及做transform动画效果
konva的Node等级如下图:

2. react中引入konva
有两种方式引入,一种是npm安装之后,使用import引入
还有一种直接在html文件的<head></head>中引入,我建议直接使用文件引入,可以使用cdn加速,并且在react的index.html中引入后,可以直接使用Konva这个全局变量
<script src="https://unpkg.com/konva@4.0.0/konva.min.js"></script>
3. 图形绘制
在react入口文件,引入绘制图形的js代码,获取canvas画布的大小后,调用绘制方法进行绘制图形。
在绘制图形前,先构造一个json数据,存放在state中:
this.state = {
data: {
"label": "Your score:",
"score": 92,
"scores": [
{ "type": "health", "score": "98" },
{ "type": "wealth", "score": "93" },
{ "type": "career", "score": "90" },
{ "type": "love", "score": "83" },
{ "type": "happiness", "score": "87" }
]
}
}
App.js所有代码如下:
import React, { Component } from 'react';
import './App.css';
import { initScene } from './tools/renderRadar.js';
class App extends Component {
constructor(props) {
super(props);
// 雷达图数据
this.state = {
data: {
"label": "Your score:",
"score": 92,
"scores": [
{ "type": "health", "score": "98" },
{ "type": "wealth", "score": "93" },
{ "type": "career", "score": "90" },
{ "type": "love", "score": "83" },
{ "type": "happiness", "score": "87" }
]
}
}
}
componentDidMount() {
const { data } = this.state;
// 获取canvas画布的宽度
const offsetWidth = document.getElementById('radar-canvas').offsetWidth;
// 绘制canvas
initScene(data, offsetWidth, offsetWidth);
}
render() {
return (
<div className="App">
<div className="demo">
<h1>Konva canvas demo:</h1>
<div className="radar-canvas" id="radar-canvas"></div>
</div>
</div>
);
}
}
export default App;
上面代码中调用 initScene来绘制canvas图像,我先简单写一下这个函数的结构
const Konva = window.Konva; let canvasHeight = 540;
let canvasWidth = 540;
// 用于获取一个可变的值,这个值和canvas画布的宽度等比例
function ratio(num){
return canvasWidth * num;
}
/**
* 绘制canvas
* @param init 雷达图数据结构
* @param offsetWidth canvas画布宽度
* @param offsetHeight canvas画布高度
* @returns {Konva.Stage}
*/
function initScene(init, offsetWidth, offsetHeight) {
// 设置画布大小
canvasHeight = offsetHeight;
canvasWidth = offsetWidth;
// 创建Konva Stage,实际上就是创建一个canvas画布
const stage = new Konva.Stage({
container: 'radar-canvas',
width: canvasWidth,
height: canvasHeight,
});
// 创建一个Konva layer
const layer = new Konva.Layer(); // todo:: 绘制雷达底图 // todo:: 绘制雷达数值图 // todo:: 绘制文字 // todo:: 绘制各角文字 // 添加layer到stage
stage.add(layer); // 绘制layer
layer.draw(); // 这里返回stage,可以用户调用函数获取画布信息,比如用户获取base64信息等
return stage;
}
注意这里有一个ratio方法,这个方法可用于设置等比的大小,用于适配各种分辨率的移动设备。
1)雷达底图绘制
雷达底图主要是使用Konva.RegularPolygon来绘制等边多边形的。
/**
* 绘制雷达地图
* @param stage
* @returns {Konva.Group}
*/
function getPentagon(stage) {
// 创建一个组,用于容纳5个大小递减的多边形,
// group的大小正好是整个canvas画布的大小
const group = new Konva.Group({
x: 0,
y: 0,
width: stage.width(),
height: stage.height(),
offsetX: 0,
offsetY: 0,
});
for (let i = 0; i < 5; i++) {
let radius = stage.width() * 0.3; // 这个为外圈的半径
radius = radius / 5 * (i + 1); // 5等分半径
// 创建一个等边多边形
const pentagon = new Konva.RegularPolygon({
x: stage.width() / 2,
y: stage.height() / 2,
sides: 5, // 边数
radius, // 半径
fill: 'transparent', // 填充颜色
stroke: '#b04119', // 边框颜色
strokeWidth: ratio(1 / 640 * 3), // 边框宽度
opacity: 0.8,
});
group.add(pentagon);
} return group;
}
在initScene函数中调用:
// 绘制雷达底图
const pentagonGroup = getPentagon(stage);
layer.add(pentagonGroup);
绘制后如下图:

2)雷达数值图绘制
使用Konva.shap可以绘制不规则的图形,实际上就是利用了canvas的moveTo, lineTo的功能:
/**
* 绘制数值图
* @param init
* @param stage
* @returns {Konva.Shape}
*/
function getValues(init, stage) {
const topics = init.scores;
// 按照实际数组大小进行360的n等分
const angle = Math.floor(360 / topics.length);
// 便宜角度,用于和雷达底图角度对齐
const offsetAngle = -angle / 4;
// 绘制不规则图形
const triangle = new Konva.Shape({
sceneFunc(context, shape) {
context.beginPath();
const startX = stage.width() / 2;
const startY = stage.height() / 2;
for (let i = 0; i < topics.length; i++) {
const value = getValuePoint(startX, startY, topics[i].score, angle * (i + 1) + offsetAngle);
if (i === 0) {
context.moveTo(value.x, value.y);
} else {
context.lineTo(value.x, value.y);
}
}
context.closePath();
context.fillStrokeShape(shape);
},
fill: '#2c00b0',
stroke: '#ffc71d',
strokeWidth: ratio(1 / 640 * 3),
opacity: 0.6,
});
return triangle;
} /**
* 根据分数获取需要移动的坐标
* @param xDef 中心点x
* @param yDef 中心点y
* @param value 数值
* @param angle 偏移角度
* @returns {{x: *, y: *}}
*/
function getValuePoint(xDef, yDef, value, angle) {
// rat为底图外圈的半径*value/100
const rat = ratio(0.3) / 100 * value;
const x = xDef + rat * Math.cos(angle * Math.PI / 180);
const y = yDef + rat * Math.sin(angle * Math.PI / 180);
return {
x,
y,
};
}
在initScene中调用方法绘制:
// 绘制雷达数值图
const values = getValues(init, stage);
layer.add(values);
绘制后图形:

3)雷达文字绘制
文字就是调用Konva.Text进行绘制,很简单,直接贴代码:
// 绘制文字
const text = new Konva.Text({
text: init.label,
fill: '#b04119',
fontSize: ratio(1 / 640 * 28),
fontStyle: 'bold italic',
fontFamily: 'Arial',
x: stage.width() / 2, // x设置为中心点
y: stage.height() / 2, // y设置为中心点
align: 'center', // 文字对齐方式
offsetY: ratio(1 / 640 * 90),
opacity: 1,
});
text.offsetX(text.width() / 2); // 对text向左偏移50%
layer.add(text);
const textScore = new Konva.Text({
text: init.score,
fill: '#ffda1d',
fontSize: ratio(1 / 640 * 160),
fontStyle: 'bold italic',
fontFamily: 'Arial',
x: stage.width() / 2,
align: 'center',
y: stage.height() / 2,
offsetY: ratio(1 / 640 * 60),
opacity: 1,
});
textScore.offsetX(textScore.width() / 2);
layer.add(textScore);
绘制后图:

4)各角文字绘制
绘制各角文字,同样利用了getValuePoint方法获取每个定点的坐标位置:
// 首字母大写
function titleCase(str) {
const arr = str.split(' ');
for (let i = 0; i < arr.length; i++) {
arr[i] = arr[i].slice(0, 1).toUpperCase() + arr[i].slice(1).toLowerCase();
}
return arr.join(' ');
} function getTopics(init, layer, stage) {
const topics = init.scores; const angle = Math.floor(360 / topics.length);
const offsetAngle = -angle / 4;
const startX = stage.width() / 2;
const startY = stage.height() / 2;
for (let i = 0; i < topics.length; i++) {
const angleCur = angle * (i + 1) + offsetAngle;
// 获取角坐标
const pointCoordinate = getValuePoint(startX, startY, 115, angleCur);
// 设置container, 每个container都以离五边形的定点15%的距离为中心点
// 宽度为画布宽度,高度为画布高度
const container = new Konva.Group({
x: pointCoordinate.x,
y: pointCoordinate.y,
width: stage.width(),
height: stage.height(),
offsetX: stage.width() / 2,
offsetY: stage.height() / 2,
}); const topic = topics[i];
// 文本
const value = titleCase(`${topic.type}:\r\n${topic.score}`);
const text = new Konva.Text({
text: value,
fill: '#671fc5',
fontSize: ratio(0.04),
fontStyle: 'bold',
fontFamily: 'Arial',
x: stage.width() / 2,
y: stage.height() / 2,
align: 'center',
offsetX: 0,
offsetY: 0,
});
// 文本向左,向上分别偏移50%,达到在container居中的效果
text.offsetX(text.width() / 2);
text.offsetY(text.height() / 2);
// 添加文字到container
container.add(text);
// 添加container到layer
layer.add(container);
}
}
在initScene中调用:
// 绘制各角文字
getTopics(init, layer, stage);
这样就得到了最终结果图:

绘制这个雷达图,多次使用了数学函数,计算左边,实际上就是利用了直角三角形边的计算方法
Math.cos()
Math.sin()
到这里,这篇文章就结束啦,后面有空,我会使用原生的canvas把这个图重新画一遍。
-------------
更新, 增加原生js脚本写的canvas图,
https://github.com/BowenHBX/konva-canvas-regularpolygon/blob/master/src/tools/renderPureRadar.js
konva canvas插件写雷达图示例的更多相关文章
- canvas 插件
http://www.jq22.com/yanshi2217 参考这个站 发现一些比较有用的canvas插件: 线形图插件:jquery.sparkline 2.1.1 excanvas 环形图,饼状 ...
- canvas制作柱形图/折线图/饼状图,Konva写动态饼状图
制作饼状图 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...
- 浅谈canvas绘画王者荣耀--雷达图
背景: 一日晚上下班的我静静的靠在角落上听着歌,这时"滴!滴!"手机上传来一阵qq消息.原来我人在问王者荣耀的雷达图在页面上如何做出来的,有人回答用canvas绘画.那么问题来了, ...
- 【带着canvas去流浪(6)】绘制雷达图
目录 一. 任务说明 二. 重点提示 三. 示例代码 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文 ...
- 带着canvas去流浪系列之六 绘制雷达图
[摘要] 用canvas原生API实现百度Echarts基本图表. 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvas ...
- HTML5 Canvas制作雷达图实战
雷达图又叫蜘蛛网图,是一种对各项数据查看很明显的表现图,在很多游戏中,对游戏中的每个角色的分析图一般也用这种图. 下面,用HTML5的Cavas来实现雷达图. 效果 一.创建Canvas var mW ...
- canvas实现平面迁徙图
前言 最近在做自己维护的一个可视化工具的时候,在添加基于echart的雷达图的时候,按照echart官网案例写完发现在自己项目中无法正常运行,排查了一番发现是我项目中echart的版本太低.找到问题原 ...
- chart.js插件生成折线图时数据普遍较大时Y轴数据不从0开始的解决办法[bubuko.com]
chart.js插件生成折线图时数据普遍较大时Y轴数据不从0开始的解决办法,原文:http://bubuko.com/infodetail-328671.html 默认情况下如下图 Y轴并不是从0开始 ...
- python批量制作雷达图
老板要画雷达图,但是数据好多组怎么办?不能一个一个点excel去画吧,那么可以利用python进行批量制作,得到样式如下: 首先制作一个演示的excel,评分为excel随机数生成: 1 =INT(( ...
随机推荐
- Python集训营45天—Day07 (面向对象编程进阶)
目录 1. @property装饰器 2. 魔法方法 3. 类属性和实例属性 4.静态方法和类方法 5. 单继承和多继承 6. 多态 7. del 方法 序言:上个章节我们了解了面向对象的基础知识,这 ...
- 生产环境轻量级dns服务器dnsmasq搭建文档
dnsmasq搭建文档 一.生产环境域名解析问题 之前生产环境设备较少,是通过维护master(192.168.1.1)设备的hosts文件实现的.每次新增设备后,需要在master的hosts文件中 ...
- 关于大脑与CPU的简单思考
今天午休突发奇想的思考了大脑与cpu的差异,发现出了大脑是生物信号驱动的单核cpu而已(并行任务是时间片的调度,要额外的堆栈记忆或者物理如纸张的存储). 大脑永远是线性的逐行执行指令,执行期间无法判断 ...
- 【Python笔记】Python 基础语法
Python 标识符 在 Python 里,标识符由字母.数字.下划线组成. 在 Python 中,所有标识符可以包括英文.数字以及下划线(_),但不能以数字开头. Python 中的标识符是区分大小 ...
- 性能测试:Jmeter-Beanshell请求加密实例
进行性能测试时,有可能遇到一种场景:接口请求由于安全问题,需要进行加密发送. 这种场景下,使用Jmeter实现性能测试,则也需要使用同样的加密规则发送请求报文. 要实现此类性能测试有几种策略: 直接去 ...
- Windows Terminal 安装及美化
windows terminal 是今年微软Build大会上推出的一款的全新终端,用来代替cmder之类的第三方终端.具有亚克力透明.多标签.Unicode支持(中文,Emoji).自带等宽字体等这些 ...
- git clone remote: HTTP Basic: Access denied
git clone 项目失败,报下面的错误信息: $ git clone http://192.168.0.141/xxxx.git Cloning into 'appEnterprise'... r ...
- 地图的标注Marker
(1)在point处添加标注:var marker = new BMap.Marker(point); (2)添加覆盖物:map.addOverlay(marker); (3)激活标注的拖拽功能:ma ...
- LitePal的存储操作
传统的存储数据方式 其实最传统的存储数据方式肯定是通过SQL语句拼接字符串来进行存储的,不过这种方式有点过于“传统”了,今天我们在这里就不讨论这种情况.实际上,Android专门提供了一种用于存储 ...
- 购买https证书以及nginx配置https
文章来源 运维公会:购买https证书以及nginx配置https 1.https的作用 https的全名是安全超文本传输协议,是在http的基础上增加了ssl加密协议.在信息传输的过程中,信息有可能 ...