iOS 畫(huà)貝塞爾曲線(xiàn) 連續(xù)曲線(xiàn) 平滑曲線(xiàn) 曲線(xiàn)圖表

利用貝塞爾曲線(xiàn)畫(huà)一段連續(xù)曲線(xiàn)

bezierDemo2.png
bezierDemo1.png
image.png

如果我們根據(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)

image.png

下面結(jié)合代碼進(jìn)行說(shuō)明

  1. 找出周一到周日對(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

完整代碼demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市居扒,隨后出現(xiàn)的幾起案子响蓉,更是在濱河造成了極大的恐慌谆趾,老刑警劉巖犹菇,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妹懒,死亡現(xiàn)場(chǎng)離奇詭異涯曲,居然都是意外死亡野哭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)幻件,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)拨黔,“玉大人,你說(shuō)我怎么就攤上這事绰沥±橛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵徽曲,是天一觀的道長(zhǎng)零截。 經(jīng)常有香客問(wèn)我,道長(zhǎng)秃臣,這世上最難降的妖魔是什么涧衙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任哪工,我火速辦了婚禮,結(jié)果婚禮上弧哎,老公的妹妹穿的比我還像新娘雁比。我一直安慰自己,他們只是感情好撤嫩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布偎捎。 她就那樣靜靜地躺著,像睡著了一般序攘。 火紅的嫁衣襯著肌膚如雪茴她。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天两踏,我揣著相機(jī)與錄音败京,去河邊找鬼兜喻。 笑死梦染,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的朴皆。 我是一名探鬼主播帕识,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼遂铡!你這毒婦竟也來(lái)了肮疗?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扒接,失蹤者是張志新(化名)和其女友劉穎伪货,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體钾怔,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碱呼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宗侦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愚臀。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖矾利,靈堂內(nèi)的尸體忽然破棺而出姑裂,到底是詐尸還是另有隱情,我是刑警寧澤男旗,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布舶斧,位于F島的核電站,受9級(jí)特大地震影響察皇,放射性物質(zhì)發(fā)生泄漏捧毛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望呀忧。 院中可真熱鬧师痕,春花似錦、人聲如沸而账。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泞辐。三九已至笔横,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咐吼,已是汗流浹背吹缔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锯茄,地道東北人厢塘。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像肌幽,于是被迫代替她去往敵國(guó)和親晚碾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容