实现样例,可生成任意多边形
代码实现
/*
@description: 多边形,雷达图,蜘蛛图canvas组件
@author: hhd (2021-12-17)
@使用方式:
导入: import myCanvas from '/common/myCanvas';
雷达图数据配置:
const configData = {
canvasW: 700, // canvas宽
canvasH: 600, // canvas高
canvasRadius: 200, // 外框半径
canvasLineWidth: 2, // 外框线宽
rBorderColor: '#999', // 外框颜色
rBorderBg: '#e8f9f0', // 外框背景颜色
rLinklineColor: '#999', // 连接线颜色
titleFont: 26, // 字体大小
titleColor: '#999', // 字体颜色
radarData : [ // 雷达图配置 radarData数量 = 雷达图数量
{
color : '#63d798', // 雷达图1颜色
lineWidth: 2, // 雷达图1线宽
isFull: false, // 是否填充背景
mData : [ // mData数量 = 雷达图边数
{ title: "诚信意愿", score: 10, fullScore: 10},
{ title: "合规履约", score: 9, fullScore: 10 },
{ title: "经营能力", score: 6, fullScore: 10 },
]
},
{
color : '#dd3239', // 雷达图2颜色
lineWidth: 2, // 雷达图2线宽
isFull: false, // 是否填充背景
mData : [
{ title: "诚信意愿", score: 10, fullScore: 10},
{ title: "合规履约", score: 1, fullScore: 10 },
{ title: "经营能力", score: 2, fullScore: 10 },
]
},
]
}
调用: myCanvas.radar(configData)
*/
var myCanvas = {
radar(configData) {
const ctx = wx.createCanvasContext("canvas");
const canvasW = configData.canvasW / this.getRatio(); // canvas宽
const canvasH = configData.canvasH / this.getRatio(); //canvas高
const L_RADIUS = configData.canvasRadius / this.getRatio(); // 大圆半径
const LINE_WIDTH = configData.canvasLineWidth / this.getRatio(); // 线宽
const rBorderColor = configData.renderBorder; // 外框颜色
const rBorderBg = configData.rBorderBg; // 外框背景色
const rLinklineColor = configData.rLinklineColor; // 连接线颜色
const titleFont = configData.titleFont; // 字体大小
const titleColor = configData.titleColor; // 字体大小
//清空画布
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.save();
// 重新映射 canvas的 (0, 0),映射的结果是让canvas的坐标原点位于 canvas的中心位
ctx.translate(canvasW / 2, canvasH / 2);
// 多边形的边数
const mCount = configData.radarData[0].mData.length;
// 需要旋转多少度,才能将多边形旋转到底边平行于 X轴,奇多边形才需要,偶多边形不需要旋转
const sAngle = (90 / mCount / 180) * Math.PI;
let rotateAngle = mCount % 2 === 0 ? 0 : sAngle * (mCount % 4 === 3 ? -1 : 1); //底边平行x轴
// 多边形外接圆半径
const lCoordinates = this.getCoordinatesByRadius(L_RADIUS,mCount,-rotateAngle);
// 绘制边框线
this.renderBorder(ctx,rBorderColor, LINE_WIDTH, L_RADIUS, -rotateAngle, mCount,rBorderBg);
// 绘制连接线
this.renderLinkLine(ctx, 0,0,lCoordinates,rLinklineColor,LINE_WIDTH);
// 绘制文字
this.drawText(ctx,lCoordinates, configData.radarData[0].mData, titleFont / this.getRatio(),titleColor);
// 绘制雷达图
configData.radarData.forEach((item) => {
console.log(item)
this.drawRadar(ctx,item.mData,L_RADIUS,-rotateAngle,item.color,item.lineWidth,item.isFull);
});
ctx.draw();
},
/**
* 获取多边形坐标
* @param mRadius 半径
* @param mCount 边数
* @param rotateAngle 旋转角度
* @return {Array}
*/
getCoordinatesByRadius(mRadius, mCount, rotateAngle = 0) {
const mAngle = (Math.PI * 2) / mCount;
let coordinates = [];
for (let i = 1; i <= mCount + 1; i++) {
let x = mRadius * Math.cos(mAngle * (i - 1) + rotateAngle);
let y = mRadius * Math.sin(mAngle * (i - 1) + rotateAngle);
coordinates.push([x, y]);
}
return coordinates;
},
/**
* 绘制边框
* @param cxt 上下文
* @param color 线框颜色
* @param lineWidth 线宽
* @param radius 半径
* @param rotateAngle 旋转角度
* @param background 背景色
*/
renderBorder(ctx,color,lineWidth,radius,rotateAngle,mCount,background) {
let coordinates = this.getCoordinatesByRadius(radius,mCount,rotateAngle);
ctx.beginPath();
coordinates.forEach((coordinate, index) => {
if (index == 0) {
ctx.moveTo(coordinate[0], coordinate[1]);
} else {
ctx.lineTo(coordinate[0], coordinate[1]);
}
});
ctx.setStrokeStyle(color);
ctx.setLineWidth(lineWidth);
ctx.stroke();
if (background) {
ctx.setFillStyle(background);
ctx.fill();
}
ctx.closePath();
},
/**
* 绘制连接线
* @param ctx 上下文
* @param centerX 中心x
* @param centerY 中心y
* @param coordinates 外边框坐标
* @param color 连线颜色
* @param lineWidth 连线宽度
*/
renderLinkLine(ctx, centerX, centerY, coordinates, color, lineWidth) {
coordinates.forEach((coordinate, index) => {
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(coordinate[0], coordinate[1]);
ctx.setStrokeStyle(color);
ctx.setLineWidth(lineWidth);
ctx.stroke();
ctx.closePath();
});
},
/**
* 绘制雷达图
* @param ctx 上下文
* @param mData 企业参数
* @param lRadius 半径
* @param rotateAngle 旋转角度
* @param color 雷达图颜色
* @param lineWidth 雷达图线宽
* @param isFill 是否填充
*/
drawRadar(ctx, mData, lRadius, rotateAngle = 0, color,lineWidth = 2, isFill = false,) {
const mCount = mData.length;
let radius = [];
mData.forEach((item, index) => {
radius.push((item.score / item.fullScore) * lRadius);
});
radius.push((mData[0].score / mData[0].fullScore) * lRadius);
const mAngle = (Math.PI * 2) / mCount;
let coordinates = [];
for (let i = 1; i <= mCount + 1; i++) {
let x = radius[i - 1] * Math.cos(mAngle * (i - 1) + rotateAngle);
let y = radius[i - 1] * Math.sin(mAngle * (i - 1) + rotateAngle);
coordinates.push([x, y]);
}
ctx.beginPath();
coordinates.forEach((coordinate, index) => {
if (index == 0) {
ctx.moveTo(coordinate[0], coordinate[1]);
} else {
ctx.lineTo(coordinate[0], coordinate[1]);
}
});
ctx.setStrokeStyle(color);
ctx.setLineWidth(lineWidth);
ctx.stroke();
if(isFill){
ctx.setFillStyle(color);
ctx.fill();
}
ctx.closePath();
this.drawCircle(ctx,coordinates,color)
},
/**
* 绘制圆点
* @param ctx 上下文
* @param coordinates 圆点坐标
* @param color 圆点颜色
*/
drawCircle: function(ctx,coordinates,color){
ctx.fillStyle = color;
ctx.beginPath();
coordinates.forEach((coordinate, index) => {
ctx.arc(coordinate[0], coordinate[1], 4, 0, Math.PI * 2, true);
ctx.closePath()
});
ctx.fill();
},
/**
* 绘制文字
* @param ctx 上下文
* @param coordinates 文字坐标
* @param mData 文字数据
* @param fontSize 文字大小
* @param color 文字颜色
*/
drawText(ctx, coordinates, mData, fontSize, color) {
const yArr = coordinates.map(coordinate => {
return coordinate[1];
});
const maxY = Math.max(...yArr); //最高点
const minY = Math.min(...yArr); // 最低点
const moveDistance = 15 / this.getRatio();
ctx.setFontSize(fontSize);
ctx.setFillStyle(color);
coordinates.forEach((coordinate, index) => {
if (mData[index]) {
let x = coordinate[0];
let y = coordinate[1];
if (maxY == coordinate[1]) {
y += moveDistance;
ctx.setTextAlign("center");
ctx.setTextBaseline("top");
} else if (minY == coordinate[1]) {
ctx.setTextBaseline("bottom");
ctx.setTextAlign("center");
y -= moveDistance;
} else if (coordinate[0] < 0) {
ctx.setTextAlign("right");
ctx.setTextBaseline("middle");
x -= moveDistance;
} else if (coordinate[0] > 0) {
ctx.setTextAlign("left");
ctx.setTextBaseline("middle");
x += moveDistance;
}
ctx.fillText(mData[index].title, x, y);
}
});
},
/**
* 获取系统信息
*/
getRatio() {
let systemInfo = wx.getSystemInfoSync();
let ratio = 750 / systemInfo.windowWidth; // rpx/px比例
return ratio;
},
}
module.exports = myCanvas;
参考文章:https://blog.csdn.net/qq_35014708/article/details/104762749