• 注册
当前位置:1313e > 默认分类 >正文

h5页面的雷达图 五边形_Flutter 五维雷达图

   referrerpolicy=

flutter 五维雷达图.png

思路:

一、绘制5个正五边形,按照五边形的半径等分绘制。至于五边形需要计算出5个定点的位置。绘制一个正五边形,一个顶点在y轴上,半径为r,s标识sin,c标识cos顺势正依次是(0,r)(rc18,rs18)(rc54,-rs54)(-rc54,-rs54)(-rc18,rs18),然后注意角度需要转换成程序需要的弧度angle / 180.0 * pi,然后使用path连接即可。

///画n个五边形

for (int i = 0; i < n; i++) {

List points = [

Offset(0, -r * (i + 1) / n),

Offset(r * (i + 1) / n * cos(angleToRadian(18)),

-r * (i + 1) / n * sin(angleToRadian(18))),

Offset(r * (i + 1) / n * cos(angleToRadian(54)),

r * (i + 1) / n * sin(angleToRadian(54))),

Offset(-r * (i + 1) / n * cos(angleToRadian(54)),

r * (i + 1) / n * sin(angleToRadian(54))),

Offset(-r * (i + 1) / n * cos(angleToRadian(18)),

r * (i + 1) / n * -sin(angleToRadian(18))),

];

drawPentagon(points, canvas, pentagonPaint);

}

///画五边形

void drawPentagon(List points, Canvas canvas, Paint paint) {

Path path = Path();

path.moveTo(0, points[0].dy);

for (int i = 1; i < points.length; i++) {

path.lineTo(points[i].dx, points[i].dy);

}

path.close();

canvas.drawPath(path, paint);

}

二、连接原点和五个定点

void drawZeroToPoint(List points, Canvas canvas) {

points.forEach((element) {

canvas.drawLine(

Offset.zero,

element,

zeroToPointPaint,

);

});

}

三、在绘制我们需要显示的雷达图,也是五边形,所以,只需要算出五个顶点就可以套用上面的绘制五边图绘制。

List list = converPoint(points, score);

drawPentagon(list, canvas, contentPaint);

List converPoint(List points, List score) {

List list = [];

for (int i = 0; i < points.length; i++) {

list.add(points[i].scale(score[i].score / 100, score[i].score / 100));

}

return list;

}

四、因为5个顶点位置的文字都需要不同的调整,所以分别设置位置

///根据位置绘制文字

for (int i = 0; i < points.length; i++) {

int type = 0;

switch (i) {

case 0:

type = 1;

points[i] -= Offset(0, padding * 2);

break;

case 1:

type = 0;

points[i] += Offset(padding, -padding);

break;

case 2:

type = 1;

points[i] += Offset(bottomPadding, padding);

break;

case 3:

type = 1;

points[i] += Offset(-bottomPadding, padding);

break;

case 4:

type = 2;

points[i] -= Offset(padding, padding);

break;

default:

}

drawText(canvas, points[i], score[i].name,

TextStyle(fontSize: 14, color: Colors.black54), type);

}

/// 右边的文字不需要移动 有的文字要移动一半居中 左边的文字需要左移动整个距离

///type 0 1 2

void drawText(Canvas canvas, Offset offset, String text, TextStyle style,

int type) {

var textPainter = TextPainter(

text: TextSpan(text: text, style: style),

textAlign: TextAlign.center,

textDirection: TextDirection.rtl);

textPainter.layout();

Size size = textPainter.size;

Offset offsetResult;

switch (type) {

case 1:

offsetResult = Offset(offset.dx - size.width / 2, offset.dy);

break;

case 2:

offsetResult = Offset(offset.dx - size.width, offset.dy);

break;

default:

offsetResult = offset;

}

textPainter.paint(canvas, offsetResult);

}

实际上思路很简单,就是需要算顶点位置,然后文字位置处理并不优雅。附上整体代码

import 'dart:math';

import 'dart:ui';

import 'package:flutter/cupertino.dart';

import 'package:flutter/material.dart';

///五维雷达图

/// 设置雷达图的半径,根据分5等分来计算

/// 绘制一个正五边形,一个顶点在y轴上,半径为r,顺势正依次是(0,r)(r*c18,r*s18)

/// (r*c54,-r*s54)(-r*c54,-r*s54)(-r*c18,r*s18)

///

class RadarBean{

double score;

String name;

RadarBean(this.score, this.name);

}

class RadarMap extends StatefulWidget {

///半径

double r = 80.0;

///正五边形个数 目前只支持五边形

int n = 5;

///文字和图像的间距

double padding = 10;

///最下面两个的间距

double bottomPadding = 8;

Paint zeroToPointPaint;

Paint pentagonPaint;

Paint contentPaint;

///当前的分数 ///对应的文案

List score = [RadarBean(100,"单位完整信息"), RadarBean(60,"单位日常管理"), RadarBean(80,"物联网感知"),

RadarBean(50,"历史火灾信息"), RadarBean( 40,"建筑消防设备")];

RadarMap(this.score,

{this.r = 80.0,

this.padding = 10,

this.bottomPadding = 8,

this.zeroToPointPaint,

this.pentagonPaint,

this.contentPaint}){

///原点到5个定点的连线

zeroToPointPaint = Paint()

..style = PaintingStyle.stroke

..color = Colors.black12

..strokeWidth = 0.5;

///5层五边形画笔

pentagonPaint = Paint()

..color = Colors.black12

..strokeWidth = 1

..style = PaintingStyle.fill;

///覆盖内容颜色

contentPaint = Paint()

..color = Colors.lightBlue[300].withAlpha(100)

..strokeWidth = 2

..style = PaintingStyle.fill;

}

@override

State createState() {

return RadarMapState();

}

}

class RadarMapState extends State {

@override

Widget build(BuildContext context) {

return Container(

color: Colors.white,

child: CustomPaint(

painter: RadarmapPainter(

widget.score,

r: widget.r,

n: widget.n,

padding: widget.padding,

bottomPadding: widget.bottomPadding,

zeroToPointPaint: widget.zeroToPointPaint,

pentagonPaint: widget.pentagonPaint,

contentPaint: widget.contentPaint),

),

);

}

}

class RadarmapPainter extends CustomPainter {

double r;

int n;

double padding;

double bottomPadding;

Paint zeroToPointPaint;

Paint pentagonPaint;

Paint contentPaint;

List score;

RadarmapPainter(this.score,

{this.r = 80.0,

this.n = 5,

this.padding = 10,

this.bottomPadding = 8,

this.zeroToPointPaint,

this.pentagonPaint,

this.contentPaint}) {

zeroToPointPaint = Paint()

..style = PaintingStyle.stroke

..color = Colors.black12

..strokeWidth = 0.5;

///5层五边形画笔

pentagonPaint = Paint()

..color = Colors.black12

..strokeWidth = 1

..style = PaintingStyle.fill;

///覆盖内容颜色

contentPaint = Paint()

..color = Colors.lightBlue[300].withAlpha(100)

..strokeWidth = 2

..style = PaintingStyle.fill;

}

@override

void paint(Canvas canvas, Size size) {

List points = [

Offset(0, -r),

Offset(r * cos(angleToRadian(18)), -r * sin(angleToRadian(18))),

Offset(r * cos(angleToRadian(54)), r * sin(angleToRadian(54))),

Offset(-r * cos(angleToRadian(54)), r * sin(angleToRadian(54))),

Offset(-r * cos(angleToRadian(18)), r * -sin(angleToRadian(18))),

];

// canvas.translate(size.width / 2, r+padding);

canvas.save();

canvas.translate(size.width / 2, size.height/2);

canvas.drawPoints(

PointMode.points,

[Offset(0, 0)],

Paint()

..color = Colors.green

..strokeWidth = 2);

///画n个五边形

for (int i = 0; i < n; i++) {

List points = [

Offset(0, -r * (i + 1) / n),

Offset(r * (i + 1) / n * cos(angleToRadian(18)),

-r * (i + 1) / n * sin(angleToRadian(18))),

Offset(r * (i + 1) / n * cos(angleToRadian(54)),

r * (i + 1) / n * sin(angleToRadian(54))),

Offset(-r * (i + 1) / n * cos(angleToRadian(54)),

r * (i + 1) / n * sin(angleToRadian(54))),

Offset(-r * (i + 1) / n * cos(angleToRadian(18)),

r * (i + 1) / n * -sin(angleToRadian(18))),

];

drawPentagon(points, canvas, pentagonPaint);

}

///连接最外层的五个定点

drawZeroToPoint(points, canvas);

///修改成对应的分数,绘制覆盖内容

List list = converPoint(points, score);

drawPentagon(list, canvas, contentPaint);

///根据位置绘制文字

for (int i = 0; i < points.length; i++) {

int type = 0;

switch (i) {

case 0:

type = 1;

points[i] -= Offset(0, padding * 2);

break;

case 1:

type = 0;

points[i] += Offset(padding, -padding);

break;

case 2:

type = 1;

points[i] += Offset(bottomPadding, padding);

break;

case 3:

type = 1;

points[i] += Offset(-bottomPadding, padding);

break;

case 4:

type = 2;

points[i] -= Offset(padding, padding);

break;

default:

}

drawText(canvas, points[i], score[i].name,

TextStyle(fontSize: 14, color: Colors.black54), type);

}

canvas.restore();

}

/// 右边的文字不需要移动 有的文字要移动一半居中 左边的文字需要左移动整个距离

///type 0 1 2

void drawText(Canvas canvas, Offset offset, String text, TextStyle style,

int type) {

var textPainter = TextPainter(

text: TextSpan(text: text, style: style),

textAlign: TextAlign.center,

textDirection: TextDirection.rtl);

textPainter.layout();

Size size = textPainter.size;

Offset offsetResult;

switch (type) {

case 1:

offsetResult = Offset(offset.dx - size.width / 2, offset.dy);

break;

case 2:

offsetResult = Offset(offset.dx - size.width, offset.dy);

break;

default:

offsetResult = offset;

}

textPainter.paint(canvas, offsetResult);

}

List converPoint(List points, List score) {

List list = [];

for (int i = 0; i < points.length; i++) {

list.add(points[i].scale(score[i].score / 100, score[i].score / 100));

}

return list;

}

void drawZeroToPoint(List points, Canvas canvas) {

points.forEach((element) {

canvas.drawLine(

Offset.zero,

element,

zeroToPointPaint,

);

});

}

///画五边形

void drawPentagon(List points, Canvas canvas, Paint paint) {

Path path = Path();

path.moveTo(0, points[0].dy);

for (int i = 1; i < points.length; i++) {

path.lineTo(points[i].dx, points[i].dy);

}

path.close();

canvas.drawPath(path, paint);

}

@override

bool shouldRepaint(CustomPainter oldDelegate) {

return false;

}

///转换角度 18/180.0 *pi

double angleToRadian(double angle) {

return angle / 180.0 * pi;

}

}

本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 162202241@qq.com 举报,一经查实,本站将立刻删除。

最新评论

欢迎您发表评论:

请登录之后再进行评论

登录