利用貝塞爾曲線(xiàn)畫(huà)一段連續(xù)曲線(xiàn)
如果我們根據(jù)幾個(gè)點(diǎn)畫(huà)一條連續(xù)的曲線(xiàn), 我們使用的方法是
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
這個(gè)方法是由一個(gè)結(jié)束點(diǎn)和兩個(gè)控制點(diǎn)作為參數(shù), 畫(huà)上一個(gè)點(diǎn)到這個(gè)結(jié)束點(diǎn)之間的一段曲線(xiàn)
首先求出控制點(diǎn)是畫(huà)各個(gè)曲線(xiàn)的必須條件, 下面這個(gè)方法是求得控制點(diǎn)的方法, 需要我們傳入4個(gè)點(diǎn), 求出控制點(diǎn)1和控制點(diǎn)2
- (void)getControlPointx0:(CGFloat)x0 andy0:(CGFloat)y0
x1:(CGFloat)x1 andy1:(CGFloat)y1
x2:(CGFloat)x2 andy2:(CGFloat)y2
x3:(CGFloat)x3 andy3:(CGFloat)y3
path:(UIBezierPath*) path{
CGFloat smooth_value =0.6;
CGFloat ctrl1_x;
CGFloat ctrl1_y;
CGFloat ctrl2_x;
CGFloat ctrl2_y;
CGFloat xc1 = (x0 + x1) /2.0;
CGFloat yc1 = (y0 + y1) /2.0;
CGFloat xc2 = (x1 + x2) /2.0;
CGFloat yc2 = (y1 + y2) /2.0;
CGFloat xc3 = (x2 + x3) /2.0;
CGFloat yc3 = (y2 + y3) /2.0;
CGFloat len1 = sqrt((x1-x0) * (x1-x0) + (y1-y0) * (y1-y0));
CGFloat len2 = sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1));
CGFloat len3 = sqrt((x3-x2) * (x3-x2) + (y3-y2) * (y3-y2));
CGFloat k1 = len1 / (len1 + len2);
CGFloat k2 = len2 / (len2 + len3);
CGFloat xm1 = xc1 + (xc2 - xc1) * k1;
CGFloat ym1 = yc1 + (yc2 - yc1) * k1;
CGFloat xm2 = xc2 + (xc3 - xc2) * k2;
CGFloat ym2 = yc2 + (yc3 - yc2) * k2;
ctrl1_x = xm1 + (xc2 - xm1) * smooth_value + x1 - xm1;
ctrl1_y = ym1 + (yc2 - ym1) * smooth_value + y1 - ym1;
ctrl2_x = xm2 + (xc2 - xm2) * smooth_value + x2 - xm2;
ctrl2_y = ym2 + (yc2 - ym2) * smooth_value + y2 - ym2;
[path addCurveToPoint:CGPointMake(x2, y2) controlPoint1:CGPointMake(ctrl1_x, ctrl1_y) controlPoint2:CGPointMake(ctrl2_x, ctrl2_y)];
}
比如我們要畫(huà)出周二到周三之間的曲線(xiàn), 我們需要傳入周一, 周二, 周三, 周四, 4個(gè)點(diǎn)的坐標(biāo), 求得周二, 周三之間的兩個(gè)控制點(diǎn), 然后畫(huà)出周二, 周三之間的曲線(xiàn), 以此類(lèi)推, 畫(huà)出每一段曲線(xiàn)
但是會(huì)發(fā)現(xiàn), 只有周一到周日7個(gè)點(diǎn)是不夠畫(huà)出6段曲線(xiàn)的, 所以我們需要在首尾各添加一個(gè)點(diǎn)(firstPoint, endPoint如下圖所示)來(lái)求出周一周二間的曲線(xiàn)和周六周日間的曲線(xiàn)
下面結(jié)合代碼進(jìn)行說(shuō)明
- 找出周一到周日對(duì)應(yīng)點(diǎn)的x, y坐標(biāo)
//初始化一個(gè)隨機(jī)y的level
_pointYArray = @[@(1), @(4), @(1), @(5), @(2), @(3), @(5)];
//確定在view中的x, y準(zhǔn)確的frame值
[_pointYArray enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSInteger objInter = 1;
if ([obj respondsToSelector:@selector(integerValue)]) {
objInter = [obj integerValue];
}
if ([obj respondsToSelector:@selector(integerValue)]) {
objInter = [obj integerValue];
if (objInter < 1) {
objInter = 1;
} else if (objInter > 5) {
objInter = 5;
}
}
CGPoint point = CGPointMake(_xAxisSpacing * idx + _axisToViewPadding + 30, CGRectGetHeight(self.frame) - _axisToViewPadding - (objInter - 1) * _yAxisSpacing - 11);
NSValue *value = [NSValue valueWithCGPoint:CGPointMake(point.x, point.y)];
[_pointsArray addObject:value];
}];
2.添加首尾兩個(gè)點(diǎn), 用于求出周一到周二和周六到周日之間控制點(diǎn)的坐標(biāo)點(diǎn)
NSValue *firstPointValue = [NSValue valueWithCGPoint:CGPointMake(_axisToViewPadding, (CGRectGetHeight(self.frame) - _axisToViewPadding) / 2)];
[_pointsArray insertObject:firstPointValue atIndex:0];
NSValue *endPointValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetWidth(self.frame), (CGRectGetHeight(self.frame) - _axisToViewPadding) / 2)];
[_pointsArray addObject:endPointValue];
3.畫(huà)曲線(xiàn), 先將起點(diǎn)移動(dòng)到周一坐標(biāo)處, 根據(jù)firstPoint, 周一, 周二, 周三坐標(biāo)畫(huà)出周一到周二的曲線(xiàn), 然后循環(huán)畫(huà)出每一段曲線(xiàn)
/** 折線(xiàn)路徑 */
UIBezierPath *path = [UIBezierPath bezierPath];
for (NSInteger i = 0; i < 6; i++) {
CGPoint p1 = [[_pointsArray objectAtIndex:i] CGPointValue];
CGPoint p2 = [[_pointsArray objectAtIndex:i+1] CGPointValue];
CGPoint p3 = [[_pointsArray objectAtIndex:i+2] CGPointValue];
CGPoint p4 = [[_pointsArray objectAtIndex:i+3] CGPointValue];
if (i == 0) {
[path moveToPoint:p2];
}
[self getControlPointx0:p1.x andy0:p1.y x1:p2.x andy1:p2.y x2:p3.x andy2:p3.y x3:p4.x andy3:p4.y path:path];
}
4.添加CAShapeLayer顯示path
/** 將折線(xiàn)添加到折線(xiàn)圖層上,并設(shè)置相關(guān)的屬性 */
_bezierLineLayer = [CAShapeLayer layer];
_bezierLineLayer.path = path.CGPath;
_bezierLineLayer.strokeColor = [UIColor redColor].CGColor;
_bezierLineLayer.fillColor = [[UIColor clearColor] CGColor];
// 默認(rèn)設(shè)置路徑寬度為0,使其在起始狀態(tài)下不顯示
_bezierLineLayer.lineWidth = 3;
_bezierLineLayer.lineCap = kCALineCapRound;
_bezierLineLayer.lineJoin = kCALineJoinRound;
[self.layer addSublayer:_bezierLineLayer];
總結(jié)
畫(huà)連續(xù)曲線(xiàn)要用到這個(gè)三次貝塞爾曲線(xiàn)的方法, 其他的畫(huà)曲線(xiàn)的方法畫(huà)出的均是不平滑的, 而畫(huà)曲線(xiàn)要求我們得出每一段之間控制曲線(xiàn)的兩個(gè)控制點(diǎn), 由四個(gè)點(diǎn)確定控制點(diǎn)的坐標(biāo), 求控制點(diǎn)方法中的smooth_value =0.6, 是用來(lái)控制曲線(xiàn)銳度的, 網(wǎng)上大多數(shù)提供的數(shù)值為0.6