微信小程序已經(jīng)發(fā)布了兩個多月了,沒有想象中那么火。但是一些擁有多用戶量的APP應用也會抽出主業(yè)務流程做微信小程序版本。做過小程序開發(fā)的小伙伴們都知道微信小程序缺少類似echart.js的圖形庫,然而業(yè)內(nèi)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函數(shù)里面調(diào)用組件
onLoad: function () {
this.radar.draw('');
}
</pre>
雷達圖組件的具體實現(xiàn)
(1)確定坐標系與中心點
canvas坐標系是X軸向右增加,Y軸向下增加的。
確定中心點:
<pre>
centerPoint = [rpx(375), rpx(375)];
context.moveTo(centerPoint[0], centerPoint[1]);
</pre>
根據(jù)雷達圖要分的比例項(即雷達圖的角數(shù))、網(wǎng)狀每一環(huán)的寬度、直角三角形的正弦sin計算網(wǎng)狀點的Y軸坐標,直角三角形的正弦cos計算網(wǎng)狀點的X軸坐標
網(wǎng)狀點的Y軸坐標(直角三角形的對角邊) = sin角度 x 網(wǎng)狀每一環(huán)的寬度(直角三角形的斜邊)
網(wǎng)狀點的X軸坐標(直角三角形的鄰角邊) = cos角度 x 網(wǎng)狀每一環(huán)的寬度(直角三角形的斜邊)
<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>
根據(jù)角度數(shù)來返回計算結果坐標偏移量的正負,是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>
計算完網(wǎng)狀交點后,用context.moveTo把對應的點連起來就可以了,最后把數(shù)據(jù)在上面顯示,即把比例點連接成一個封閉圖形填充一個辦透明的顏色就可以了:
<pre>
// 繪制的雷達比例數(shù)據(jù)
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與計算坐標點可以繪畫很多定制化需求的圖形。
您的意見是我改善的東西,歡迎評論提建議,如果對您有幫助,請點個贊,謝謝~~
菲麥前端專題,匯聚前端好文,邀您關注!