一日晚上下班的我静静的靠在角落上听着歌,这时"滴!滴!"手机上传来一阵qq消息。原来我人在问王者荣耀的雷达图在页面上如何作出来的,有人回答用canvas绘画。那么问题来了,已经很久没有使用canvas绘画了东西。 SO,就想本身画一个canvas雷达图,顺便从新回顾一下canvas的知识点。git
聊天记录当中的雷达图不是特别清楚,因此我这边截图了本身的一个战绩雷达图。 github
在绘画这个正六边形的时候,先让咱们对于这个正六边形进行简单的数学分析。 这里先用画板画一个正六变形,而后进行切割并切角。 canvas
是吧,借用之前高中仍是初中的数学,正六边形的内角和720°
,那么每个对角就是120°
。在已知对角线的长度。那么经过sin60°
,cos60°
一类的,那个能够求出各个三角形的边长。数组
但是问题来了,这里咱们要计算的是各个坐标点。而canvas的坐标轴是从左上角算(0,0)原点的单象限坐标轴。假设六边形的中心点是(250,250)、对角线的长度是100*2,那么按照三角函数推断: bottom-center
坐标:(250, 250 + 100)
bottom-left
坐标:(250 - 100*sin(60°), 250+100*cos(60°))
top-left
坐标:(250 - 100*sin(60°), 250-100*cos(60°))
top-center
坐标:(250, 250 - 100)
top-right
坐标:(250 + 100*sin(60°), 250-100*cos(60°))
bottom-right
的坐标:(250 + 100*sin(60°), 250+100*cos(60°))
浏览器
坐标是出来了,可是一个点一个点去绘画是否是有点太low了! 肿么办? 啦啦啦啦! 那么就到了咱们找规律的时间来了!bash
可是在找规律的同时,为毛中心点的X
轴和别人不同,为毛一会加一会减。网络
因此当思考各坐标点参数的规律的时候,让先回顾之前的函数角度图表 函数
看完这个函数参照图以后,让我再次修改一下6个点的书写方式。 bottom-center
坐标:(250 + 100*sin(0°), 250 + 100*cos(0°))
bottom-left
坐标:(250 + 100*sin(300°), 250+100*cos(300°))
top-left
坐标:(250 + 100*sin(240°), 250-100*cos(240°))
top-center
坐标:(250 +100*sin(180°), 250 + 100*cos(180°))
top-right
坐标:(250 + 100*sin(120°), 250-100*cos(120°))
bottom-right
的坐标:(250 + 100*sin60°), 250+100*cos(60°))
字体
这个时候再看组坐标数据点,是否是感受有点意思!spa
那么这个时候咱们即可以经过一个for循环,用一个数组把这6个坐标点给记录下来。
var pointArr = [];
for (var i = 0; i < 6; i++) {
pointArr[i] = {};
pointArr[i].x = 250 + 100 * Math.sin(60 * i);
pointArr[i].y = 250 + 100* Math.cos(60 * i);
}
复制代码
前面既然,将正六边形的坐标点经过一个for循环解析出来。那么就是代码绘画正六边形了:
<style>
canvas {
display: block;
width: 500px;
height: 500px;
}
</style>
<body>
<canvas class="radar"></canvas>
</body>
<script>
var canvas = document.getElementsByClassName('radar')[0];
canvas.width = 500;
canvas.height = 500;
var ctx = canvas.getContext('2d');
ctx.save();
ctx.strokeStyle = '#888888'; // 设置线条颜色
var lineArr = [];
var rAngle = Math.PI * 2 / 6; // 算出每个内角和
console.log(rAngle);
var rCenter = 250; // 肯定中心点
var curR = 100; // 肯定半径长度
ctx.beginPath();
for (var i = 0; i < 6; i++) {
lineArr[i] = {};
lineArr[i].y = rCenter + curR * Math.cos(rAngle * i);
lineArr[i].x = rCenter + curR * Math.sin(rAngle * i);
ctx.lineTo(lineArr[i].x, lineArr[i].y);
}
ctx.closePath();
ctx.stroke();
ctx.restore();
复制代码
哈哈!!感受发现了新大陆了!绘制正多边形的貌似能够按照这个规律来!!
既然前面有一个数组存储各个坐标点,因此让每一个对角线对角点直线想连就ok了!
ctx.strokeStyle = '#e8ddc7'; // PS吸管那么一吸
ctx.save();
ctx.beginPath();
// for (var j = 0; j < 3; j++) {
// ctx.lineTo(lineArr[j].x, lineArr[j].y);
// ctx.lineTo(lineArr[j+3].x, lineArr[j+3].y);
// ctx.stroke();
// }
for (var j = 0; j < 3; j++) {
ctx.moveTo(lineArr[j].x, lineArr[j].y);
ctx.lineTo(lineArr[j + 3].x, lineArr[j + 3].y);
ctx.stroke();
}
ctx.closePath();
ctx.restore();
复制代码
关于数据填充区,也就是雷达图当中,不规则的红色半透明的六边形。其实就是就能够看作中心点,到各个边角点之间线段为一区间这。以后就是将这个区间分红若干份,你占这个这个区间多少份,满份就是边角点,零份就是原点。
观察前面的雷达图当中,B等级大概占据某个等级的50%左右。而B前面还有等级A、S。 因此当S等级时候,能够看做区间 / 1。 B等级看做区间 / 2, 那么A就是 区间 / 1.5. 以此类推就能够得出剩下 C 就是区间 / 2.五、D:区间/ 3
这里我就不用for循环书写了,直接偷懒手写一个对象。
// 绘制数据区域
var letterData = {
'S': 1,
'A': 1.5,
'B': 2,
'C': 2.5,
'D': 3
}
ctx.save();
ctx.beginPath();
for (var i = 0; i < 6; i++) {
lineArr[i].yEnd = rCenter + curR * Math.cos(rAngle * i) / (letterData[rData[i][1]]);
lineArr[i].xEnd = rCenter + curR * Math.sin(rAngle * i) / (letterData[rData[i][1]]);
ctx.lineTo(lineArr[i].xEnd, lineArr[i].yEnd);
console.log(lineArr);
}
ctx.closePath();
ctx.stroke();
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
ctx.fill();
复制代码
当咱们回归到前面的截图发现,须要单独把数据填充区域的的各个点位置给增强,并把边角用更深的线条的描绘出来。
ctx.lineWidth = 2; //设置数据填充区域的线条颜色
ctx.strokeStyle = '#dd3f26'; //设置填充区域的颜色
var point = 3; //设置数据填充区域的小圆点大小
for (var i = 0; i < 6; i++) {
ctx.beginPath();
ctx.arc(lineArr[i].xEnd, lineArr[i].yEnd, point, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.8)';
ctx.fill();
console.log(lineArr);
}
ctx.restore();
复制代码
王者荣耀雷达文本是须要绘制两点,
①用黑色16px字体绘制各点描述点 ②用红色30px字体绘制各点能力级别
可是估计看到绘制文本,估计有的小伙伴就会说。不是有数组的存储各个边角的坐标,直接一个for循环依次根据各个点绘画出来不就OK了。
// 绘制文本
var rData = [
['生存', 'S'],
['经济', 'S'],
['输出', 'S'],
['KDA', 'B'],
['打野', 'B'],
['推动', 'S']
]
ctx.save();
ctx.font = '16px Microsoft Yahei'; //设置字体
ctx.fillStyle = '#000'; // 颜色
for (var i = 0; i < 6; i++) {
var y = rCenter + curR * Math.cos(rAngle * i);
var x = rCenter + curR * Math.sin(rAngle * i);
ctx.fillText(rData[i][0], x, y);
}
ctx.restore();
复制代码
浏览器最终显示的视觉效果:
是否是以为很惊喜,这里输出
、经济
位置勉强还行,可是剩下的文字位置就误差了许多了。因此在绘制文字的时候,还得针对文字的坐标位置进行相应的调整。
既然直接调用坐标的位置会出问题,那么让根据上文中的图片文字的规则简单分析。 ①若是X轴
== 中心点,那么就判断Y轴
。比中心点大文字下移一点,反之文字上移一点。 ②若是X轴
< 中心点,那么文字X轴位置就左移动一点,反正右移动距离。
// 绘制文本
ctx.save();
var fontSize = 16;
ctx.font = fontSize + 'px Microsoft Yahei';
ctx.textBaseline="middle"; //设置基线参考点
ctx.textAlign="center"; // 文本居中
ctx.fillStyle = '#000';
for (var i = 0; i < 6; i++) {
var y = rCenter + curR * Math.cos(rAngle * i);
var x = rCenter + curR * Math.sin(rAngle * i);
console.log(Math.sin(rAngle * i))
var s_width = ctx.measureText(rData[i][0]).width; //获取当前绘画的字体宽度
if ( x == rCenter) {
if (y > rCenter ) {
ctx.fillText(rData[i][0], x - s_width/2, y + fontSize);
} else {
ctx.fillText(rData[i][0], x - s_width/2, y - fontSize);
}
} else if ( x > rCenter) {
console.log(rData[i][0]);
ctx.fillText(rData[i][0], x + s_width*1.5, y);
} else {
ctx.fillText(rData[i][0], x - s_width*1.5, y);
}
复制代码
这里多了好几个不经常使用的属性,下面就是介绍这些属性的特色: ctx.textBaseline
: 设置或返回在绘制文本时使用的当前文本基线 说到基线,各位童鞋想想我们之前英文练习本,上面有着一条条线条
瞬间回忆到当年被罚抄英语单词的岁月,一把辛酸泪呀。
网页设计字体也有一个基线的存在,所以canvas的基线点就是直接从坐标点划出一条横线基线。 这里从网络上截图一张,经过设置基线参考位置,看看文本所在位置的改变。
ctx.textAlign
: 这个文本水平居中,不过和CSS当中的居中不同的是,他是从坐标点划出一条竖线分割文本的。
ctx.measureText
: 返回包含指定文本宽度的对象。
通俗一点的就是说,就是获取你绘制文本的宽度。假设一排文字内容为'Hello World', size为16px大小文本。在这里高度都是16px稳定不变,这样canvas画其余元素对这个位置只须要Y轴
移动这个文本的'size'大小就能够避免覆盖到上面。
可是若是要X
轴去移动位置,你根本不知道'Hello World'这串文本的长度。那么这个时候就须要ctx.measureText这个方法,获取当前你绘制文本的宽度。
既然前面已经介绍了描述的绘画方法,那么依葫芦画瓢。让咱们一并开始绘制能力级别的文本。
// 绘制文本
ctx.save();
var fontSize = 16;
var maxfontSize = 30;
ctx.font = fontSize + 'px Microsoft Yahei';
ctx.textBaseline="middle";
ctx.textAlign="center";
for (var i = 0; i < 6; i++) {
var y = rCenter + curR * Math.cos(rAngle * i);
var x = rCenter + curR * Math.sin(rAngle * i);
console.log(Math.sin(rAngle * i))
var s_width = ctx.measureText(rData[i][0]).width;
if ( x == rCenter) {
if (y > rCenter ) {
ctx.fillText(rData[i][0], x - s_width/2, y + fontSize);
} else {
ctx.fillText(rData[i][0], x - s_width/2, y - fontSize);
}
} else if ( x > rCenter) {
console.log(rData[i][0]);
ctx.fillText(rData[i][0], x + s_width*1.5, y);
} else {
ctx.fillText(rData[i][0], x - s_width*1.5, y);
}
}
ctx.restore();
ctx.save();
// 绘制等级
ctx.font = '30px Microsoft Yahei bold';
ctx.fillStyle = '#d7431f';
ctx.textBaseline="middle";
ctx.textAlign="center";
for (var i = 0; i < 6; i++) {
var y = rCenter + curR * Math.cos(rAngle * i);
var x = rCenter + curR * Math.sin(rAngle * i);
var M_width = ctx.measureText(rData[i][1]).width;
if ( x == rCenter) {
if (y > rCenter ) {
ctx.fillText(rData[i][1], x + M_width/2, y + fontSize);
} else {
ctx.fillText(rData[i][1], x + M_width/2, y - fontSize);
}
} else if ( x > rCenter) {
console.log(rData[i][0]);
ctx.fillText(rData[i][1], x + M_width, y);
} else {
ctx.fillText(rData[i][1], x - M_width, y);
}
}
ctx.restore();
ctx.save();
复制代码
页面最终效果:
好了!以上就是鄙人对于canvas绘画一点简单理解与复习了,其中也回顾了一些canvas基本属性点。后续如何用canvas玩出各类花样就看各位看官本身了!
小贴士: 在使用ctx.measureText
这个方法的时候须要注意一下。这个方法在宽度参考对象也跟当前绘画环境的font-size有关联的。
打个比方说,在绘制描述的文本的时候。font-size设置是16px,那么ctx.measureText('输出').width 是32。 那么在绘制能力等级的时候,font-size设置是32,那么ctx.measureText('输出').width 就再也不是32了而是64或者。
原创文章,文笔有限,才疏学浅,文中如有不正之处,再次再次再次欢迎各位啪啪的打脸赐教。(有句话说的好,重要的词得说三遍。)
原文写于17年12月07日, 现搬入【切图仔平常】
PS:对于里面完整blogDemo代码感兴趣的,能够从本人github上阅览。
我是车大棒!我为我本身带眼, 我为我本身带盐!
(阿西吧,之前每天文章底部扯为本身带眼。而后结果今年体检以后,再去趟医院以后就得每天随身带盐了。我特么。。。)
诸位, 身体才是才是革命的本钱。