最近公司要做一個醫(yī)學(xué)考試類的App遇汞,里面有一個能力分析的雷達(dá)圖最筒,leader讓我來封裝這個雷達(dá)圖颜凯。這個模塊是在做測心率模塊期間完成的蹲缠,也是使用的
CoreGraphics
來實現(xiàn)的。
之前并不知道雷達(dá)圖長什么樣唉侄,這個是我在百度上下載的咒吐。大概就長這個樣子,我們要用的和這個不太一樣,沒有文字和介紹恬叹。
思路
寫這個的時候候生,沒有查閱什么資料,所以不太清楚別人是怎么做的绽昼。
我是從角度出發(fā)的唯鸭,例如上面的圖中,有5個能力模塊硅确,5個能力模塊的分支(后面就叫它主干
了目溉,我也不知道叫什么)兩兩的夾角都是相同的。
- 首先把一個主干放在
y軸
上菱农,這個主干繞原點 O
一周是2π
,被5個主干
5等分缭付,那么每一個夾角都是2π/5
。 - 主干的長是相同的循未,可以通過
正弦
和余弦
來求出每個主干落點的坐標(biāo),再從原點O
到落點
畫線蛉腌。 - 之后的網(wǎng)格和能力分布的畫法也是這個思路,同樣是從
y軸
上的主干取點只厘,不同的是斜邊
的長度會根據(jù)網(wǎng)格間距
和能力百分比
變化烙丛,如下圖:
不知道大家還記不記得正弦和余弦是啥了,我是已經(jīng)還給數(shù)學(xué)老師了(雖然初中數(shù)學(xué)也是年組第一的)羔味,剛好寫這個的前一天在看高數(shù)河咽。。
正弦: sin? = 對邊/斜邊
余弦: cos? = 臨邊/斜邊
3實現(xiàn)
1. 初始化赋元,定義幾個屬性后面使用
// 元素數(shù)組
@property (strong, nonatomic) NSArray <Element *>*elements;
這是一個放Model的數(shù)組忘蟹,用來儲存能力模塊信息
// 每一個主干的長度
static float element_w = 0;
// 主干到View預(yù)留的空白部分寬度
static float border_w = 5;
// 中心點的橫坐標(biāo)
static float center_w = 0;
// 中心點的縱坐標(biāo)
static float center_h = 0;
- (instancetype)initWithFrame:(CGRect)frame Elements:(NSArray <Element *>*)elements {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor whiteColor];
self.elements = elements;
if (self.frame.size.width>self.frame.size.height)
element_w = (self.frame.size.height-border_w*2)/2;
else
element_w = (self.frame.size.width-border_w*2)/2;
center_w = self.frame.size.width/2;
center_h = self.frame.size.height/2;
}
return self;
}
2.畫主干
- (void)buildRadarMap {
if (self.elements.count<3) return;
// 獲取畫布
CGContextRef context = UIGraphicsGetCurrentContext();
// 劃線顏色
if (self.trunkLineColor)
CGContextSetStrokeColorWithColor(context, self.trunkLineColor.CGColor);
else
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 劃線寬度
if (self.lineWidth)
CGContextSetLineWidth(context, self.lineWidth);
else
CGContextSetLineWidth(context, 1);
// 起點坐標(biāo)
CGContextMoveToPoint(context, center_w, center_h);
// 第一條線
CGContextAddLineToPoint(context, center_w, center_h - element_w);
//畫元素主干
for (int i = 1; i <self.elements.count; i++) {
float x = 0;
float y = 0;
double pi = (M_PI*2.0/(self.elements.count))*i;
// 計算主干落點坐標(biāo)
Coordinate(pi, element_w, center_w, center_h,&x, &y);
// 設(shè)置每次的初始點坐標(biāo)
CGContextMoveToPoint(context, center_w, center_h);
// 設(shè)置終點坐標(biāo)
CGContextAddLineToPoint(context, x, y);
}
CGContextStrokePath(context);
}
注:self.trunkLineColor
是我設(shè)置的一個外部可控主干的顏色,如果尾部沒有設(shè)置就給默認(rèn)值搁凸。self.lineWidth
就是先的寬度了媚值。
/**
* 雷達(dá)圖分成幾等份 默認(rèn)1等份(最外面一圈的框框)
*/
@property (assign, nonatomic) float part;
/**
* 雷達(dá)線的寬度 默認(rèn)1
*/
@property (assign, nonatomic) float lineWidth;
/**
* 主干線顏色 默認(rèn)黑色
*/
@property (strong, nonatomic) UIColor *trunkLineColor;
/**
* 分等份線顏色 默認(rèn)黑色
*/
@property (strong, nonatomic) UIColor *partLineColor;
/**
* 比例線顏色 默認(rèn)綠色
*/
@property (strong, nonatomic) UIColor *percentLineColor;
- (void)setTrunkLineColor:(UIColor *)trunkLineColor {
_trunkLineColor =trunkLineColor;
[self setNeedsDisplay];
}
- (void)setPartLineColor:(UIColor *)partLineColor {
_partLineColor = partLineColor;
[self setNeedsDisplay];
}
- (void)setPercentLineColor:(UIColor *)percentLineColor {
_percentLineColor = percentLineColor;
[self setNeedsDisplay];
}
設(shè)置set方法調(diào)用setNeedsDisplay
外部設(shè)置顏色的時候會調(diào)用drawRect:
3.計算落點的方法
#pragma mark - 算落點坐標(biāo)
void Coordinate (double pi, float l, float c_w , float c_h, float *x, float *y) {
if (pi < M_PI_2) {
*x = c_w + sin(pi)*l;
*y = c_h - cos(pi)*l;
} else if (pi == M_PI_2) {
*x = c_w + sin(pi)*l;
*y = c_h + cos(pi)*l;
} else if (pi < M_PI) {
*x = c_w + sin(pi)*l;
*y = c_h - cos(pi)*l;
} else if (pi == M_PI) {
*x = c_w + sin(pi)*l;
*y = c_h + cos(pi)*l;
} else if (pi < M_PI_2*3) {
*x = c_w + sin(pi)*l;
*y = c_h - cos(pi)*l;
} else if (pi == M_PI_2*3) {
*x = c_w + sin(pi)*l;
*y = c_h + cos(pi)*l;
} else {
*x = c_w + sin(pi)*l;
*y = c_h - cos(pi)*l;
}
}
變量 | 用途 |
---|---|
pi |
角度 |
l |
主干長度 |
c_w |
原點橫坐標(biāo) |
c_h |
原點縱坐標(biāo) |
x |
落點橫坐標(biāo) |
y |
落點縱坐標(biāo) |
坐標(biāo)系和View的Rect還不太一樣,比如在坐標(biāo)系第二护糖、三象限的y
是負(fù)值褥芒,而我們要的坐標(biāo)的y
是越往下越大,所以我們要c_h+(-y)
嫡良,x
同理锰扶。
4.畫網(wǎng)格,也就是要把主干分成幾等分寝受,為了更靈活我也設(shè)置了可以從外部設(shè)置坷牛。
/**
* 雷達(dá)圖分成幾等份 默認(rèn)1等份(最外面一圈的框框)
*/
@property (assign, nonatomic) float part;
- (void)setPart:(float)part {
_part = part;
[self setNeedsDisplay];
}
#pragma mark - 畫雷達(dá)分等分圖
- (void)buildPart {
float r = self.part<=1?1:self.part;
// 獲取畫布
CGContextRef context = UIGraphicsGetCurrentContext();
// 劃線顏色
if (self.partLineColor)
CGContextSetStrokeColorWithColor(context, self.partLineColor.CGColor);
else
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 劃線寬度
if (self.lineWidth)
CGContextSetLineWidth(context, self.lineWidth);
else
CGContextSetLineWidth(context, 1);
// 話分割線
for (int j = 0; j<r; j++) {
// 設(shè)置每次的初始點坐標(biāo)
CGContextMoveToPoint(context, center_w, border_w);
// 畫百分比分部
for (int i = 1; i<=self.elements.count; i++) {
float x = 0;
float y = 0;
double pi = (M_PI*2.0/(self.elements.count))*i;
Coordinate(pi,element_w*(r-j)/r, center_w, center_h,&x, &y);
if (i == 1) {
CGContextMoveToPoint(context, center_w, border_w + element_w*j/r);
}
if (i == self.elements.count) {
CGContextAddLineToPoint(context, center_w, border_w + element_w*j/r);
} else {
CGContextAddLineToPoint(context, x, y);
}
}
}
CGContextStrokePath(context);
}
原理和畫主干是一樣的,只不過是每次的起點
變成了上一個點的落點
很澄。把主干
的長度進(jìn)行了分割
5.畫能力占比線
#pragma mark - 畫百分比占比線
- (void)buildPercent {
// 獲取畫布
CGContextRef context = UIGraphicsGetCurrentContext();
// 劃線顏色
if (self.percentLineColor)
CGContextSetStrokeColorWithColor(context, self.percentLineColor.CGColor);
else
CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
// 劃線寬度
if (self.lineWidth)
CGContextSetLineWidth(context, self.lineWidth);
else
CGContextSetLineWidth(context, 1);
Element *ele = self.elements[0];
CGContextMoveToPoint(context, center_w, border_w +element_w*(1-ele.percent));
for (int i = 1; i<=self.elements.count; i++) {
float x = 0;
float y = 0;
if (i == self.elements.count) {
//終點京闰,最終回到開始點坐標(biāo)
Element *ele = self.elements[0];
CGContextAddLineToPoint(context, center_w, border_w +element_w*(1-ele.percent));
} else {
Element *ele = self.elements[i];
double pi = (M_PI*2.0/(self.elements.count))*i;
Coordinate(pi,element_w*ele.percent, center_w, center_h,&x, &y);
CGContextAddLineToPoint(context, x, y);
}
}
CGContextStrokePath(context);
}
這里和畫網(wǎng)格不一樣的就是每次的把主干的長度按照這個能力值
的百分比
分割
6.總結(jié)
因為有之前的心率那得基礎(chǔ)颜及,這里完成的很快,然后做了一個相對比較靈活的封裝蹂楣,還沒有加能力描述和百分比值俏站,后續(xù)加上。
代碼地址:https://github.com/YvanLiu/RadarMap.git
歡迎吐槽捐迫,歡迎指正乾翔,歡迎批評爱葵,歡迎提問施戴。
7.更新
9.23 11:43
重新封裝了一個雷達(dá)圖,這次的會比較全面萌丈。封裝后的效果圖
代碼地址還是之前的