微信小程序已经发布了两个多月了,没有想象中那么火。但是一些拥有多用户量的APP应用也会抽出主业务流程做微信小程序版本。做过小程序开发的小伙伴们都知道微信小程序缺少类似echart.js的图形库,然而业内echart.js等主流的h5图形库并不能在小程序上运行,所以遇到绘制图形需求的时候我们只能用canvas绘画。本文为大家分享的是以自定义组件的形式绘制雷达图。
自定义组件
(1)建立组件文件
在工程根目录建立component文件夹,里面建radar目录,在radar里面建radar.js、radar.wxml、radar.wxss,3个文件
radar.js写一个导出的方法:
<pre>
module.exports = {
radar
};
</pre>
radar.wxml写一个模板:
<pre>
<template name="radar">
<view class="radar">
<canvas class="canvas" canvas-id="radarCanvas" />
</view>
</template>
</pre>
radar.wxss写对应的样式:
<pre>
.radar canvas {
width: 750rpx;
height: 750rpx;
background-color: #6fb9de;
}
</pre>
(2)在页面中引入组件
比如我们要在index页面引入组件,先在index.wxss引入样式文件:
<pre>
@import "../../component/radar/radar.wxss"
</pre>
在index.wxml引入模板文件:
<pre>
<import src="../../component/radar/radar.wxml"/>
<template is="radar"/>
</pre>
在index.js引入js逻辑文件:
<pre>
// 1 引入雷达图
import { radar } from '../../component/radar/radar'
// 2 page对象里加入组件
Page({
radar,
// 3 在onLoad函数里面调用组件
onLoad: function () {
this.radar.draw('');
}
</pre>
雷达图组件的具体实现
(1)确定坐标系与中心点
canvas坐标系是X轴向右增加,Y轴向下增加的。
确定中心点:
<pre>
centerPoint = [rpx(375), rpx(375)];
context.moveTo(centerPoint[0], centerPoint[1]);
</pre>
根据雷达图要分的比例项(即雷达图的角数)、网状每一环的宽度、直角三角形的正弦sin计算网状点的Y轴坐标,直角三角形的正弦cos计算网状点的X轴坐标
网状点的Y轴坐标(直角三角形的对角边) = sin角度 x 网状每一环的宽度(直角三角形的斜边)
网状点的X轴坐标(直角三角形的邻角边) = cos角度 x 网状每一环的宽度(直角三角形的斜边)
<pre>
for(n = 0; n < layerNum; n++) {
layerPoints[n] = [];
for(k = 0; k < angleNum; k++) {
context.moveTo(centerPoint[0], centerPoint[1]);
let offsetX = layerWidth * (n + 1) * getXParam(angleAvg * (k + 1) + angleOffset);
let offsetY = layerWidth * (n + 1) * getYParam(angleAvg * (k + 1) + angleOffset);
let distX = centerPoint[0] + offsetX;
let distY = centerPoint[1] + offsetY;
if(n == layerNum - 1) {
context.lineTo(distX, distY);
if(wordArr[k]) {
let wordOffsetX = offsetX >= 0 ? 1 : -1;
wordOffsetX = distX + wordOffsetX * wordOffset[k][0];
let wordOffsetY = offsetY >= 0 ? 1 : -1;
wordOffsetY = distY + wordOffsetY * wordOffset[k][1];
context.fillText(wordArr[k], wordOffsetX, wordOffsetY);
}
}
layerPoints[n][k] = [distX, distY];
}
}
</pre>
根据角度数来返回计算结果坐标偏移量的正负,是x轴是在左还是右,y轴是上还是下:
<pre>
let getXParam = (angle) => {
let param = 1;
if(angle >= 0 && angle < 90) {
param = 1;
} else if(angle >= 90 && angle < 180) {
param = -1;
angle = 180 - angle;
} else if(angle >= 180 && angle < 270) {
param = -1;
angle = angle - 180;
} else if(angle >= 270 && angle <= 360) {
param = 1;
angle = 360 - angle;
}
let angleCos = Math.cos(Math.PI / 180 \* angle);
if(angleCos < 0) {
angleCos = angleCos \* -1;
}
return angleCos \* param;
};
let getYParam = (angle) => {
let param = 1;
if(angle >= 0 && angle < 90) {
param = 1;
} else if(angle >= 90 && angle < 180) {
param = 1;
angle = 180 - angle;
} else if(angle >= 180 && angle < 270) {
param = -1;
angle = angle - 180;
} else if(angle >= 270 && angle <= 360) {
param = -1;
angle = 360 - angle;
}
let angleSin = Math.sin(Math.PI / 180 \* angle);
if(angleSin < 0) {
angleSin = angleSin \* -1;
}
return angleSin \* param;
};
</pre>
计算完网状交点后,用context.moveTo把对应的点连起来就可以了,最后把数据在上面显示,即把比例点连接成一个封闭图形填充一个办透明的颜色就可以了:
<pre>
// 绘制的雷达比例数据
let dataArr = [5, 2, 6, 2, 6, 0, 3];
// 绘制比例:
context.beginPath();
context.setStrokeStyle("rgba(77,168,213,0.85)");
context.setFillStyle("rgba(77,168,213,0.85)");
let isFirstPoint = true;
let tmpPoints = [];
for(m = 0; m < angleNum; m++) {
tmpPoints = centerPoint;
if(dataArr[m] > 0) {
for(n = 0; n < layerNum; n++) {
if(dataArr[m] == (n + 1)) {
tmpPoints = layerPoints[n][m];
break;
}
}
}
if(isFirstPoint) {
context.moveTo(tmpPoints[0], tmpPoints[1]);
isFirstPoint = false;
} else {
context.lineTo(tmpPoints[0], tmpPoints[1]);
}
}
context.fill();
context.stroke();
context.closePath();
</pre>
微信小程序雷达图完整代码:
https://github.com/zhangxiongwu/smallAppRadar
结语:熟悉绘画canvas与计算坐标点可以绘画很多定制化需求的图形。
您的意见是我改善的东西,欢迎评论提建议,如果对您有帮助,请点个赞,谢谢~~
菲麦前端专题,汇聚前端好文,邀您关注!