iOS 沿曲线线性渐变的贝塞尔曲线

更新:换一种思路实现。iOS 沿曲线线性渐变的贝塞尔曲线(改进版)

iOS原生的渐变只支持线性的渐变,但有的时候我们需要沿曲线进行渐变。
先看下垂直线性渐变与沿曲线线性渐变的区别


垂直线性渐变:颜色最亮的地方在曲线的最低点

沿曲线线性渐变:颜色最亮的地方在起点

那么先来分析一下这个问题:

  1. 怎样绘制曲线?
    对于贝塞尔曲线的绘制,系统提供了一系列的方法,同时我们已可以通过公式计算出一条贝塞尔曲线。
  2. 如何保证颜色渐变?
    找到曲线上的点,计算出每一个点的色值。

只要解决上面的问题就可以画出一条沿曲线线性渐变的贝塞尔曲线,曲线画起来还是比较简单的,但是这样计算出每一点的色值是一件比较麻烦的事情。

1、贝塞尔曲线

这里先介绍一下贝塞尔曲线的一些东西,以二次贝塞尔曲线为例,先来动态感受一下绘制过程

二次贝塞尔曲线

一条二次贝塞尔曲线需要三个点,A:起点 ;B:控制点;C:终点


然后在AB上取点E,在BC上取点F 。使AD:AB = BE:BC

第一次取点

在DE上取点F,使DF:DE = AD:AB = BE:BC


第二次取点

F点就是贝塞尔曲线上的一个点,以此类推,取点一系列的点之后在ABC之间就产生了一条贝塞尔曲线

贝塞尔曲线

可以看出贝塞尔曲线上的每个点是有规律的,二次贝塞尔曲线的方程为
P0:起点;P1:控制点; P2:终点 ;t:百分比

二次贝塞尔曲线的方程

用OC表达的话就是这样的

CGFloat x = pow((1-t), 2) * _startPoint.x + 2 * (1-t) * t * _controlPoint.x + pow(t, 2) * _endPoint.x;
CGFloat y = pow((1-t), 2) * _startPoint.y + 2 * (1-t) * t * _controlPoint.y + pow(t, 2) * _endPoint.y;

2、渐变色

既然已经一颗贝塞尔曲线的方程,那就可以操作曲线上的每一个点了,那怎么设置每一个点的颜色的。

1、取点

对于取点还有一个问题需要注意,由于贝塞尔曲线并不是匀速变化的,所有如果均匀分割 t 来进行取点的话,取出来的点是不均匀的。不均匀的点会造成有的地方缺失点,形成空白。所以需要对 t 进行修正,取出间隔均匀的点。

均匀间隔的 t

想要均匀的点,就需要线计算出曲线长度,接下来就使用辛普森积分法来计算曲线的长度。这个求的是二次贝塞尔曲线的长度,如果需更高次的曲线,可修改一下修改。

//曲线长度
- (CGFloat)lengthWithT:(CGFloat)t{
    NSInteger totalStep = 1000;
    
    NSInteger stepCounts = (NSInteger)(totalStep * t);
    
    if(stepCounts & 1) stepCounts++;
    
    if(stepCounts==0) return 0.0;
    
    NSInteger halfCounts = stepCounts/2;
    CGFloat sum1=0.0, sum2=0.0;
    CGFloat dStep = (t * 1.0)/stepCounts;
    for(NSInteger i=0; i<halfCounts; i++) {
        sum1 += [self speedAtT:(2*i+1)*dStep];
    }
    
    for(NSInteger i=1; i<halfCounts; i++) {
        sum2 += [self speedAtT:(2*i)*dStep ];
    }
    return ([self speedAtT:0]+[self speedAtT:1]+2*sum2+4*sum1)*dStep/3.0;
}

- (CGFloat)speedAtT:(CGFloat)t {
    CGFloat xSpeed = [self xSpeedAtT:t];
    CGFloat ySpeed = [self ySpeedAtT:t];
    CGFloat speed = sqrt(pow(xSpeed, 2) + pow(ySpeed, 2));
    return speed;
}

- (CGFloat)xSpeedAtT:(CGFloat)t {
    return 2 * (_startPoint.x + _endPoint.x - 2 * _controlPoint.x) * t + 2 * (_controlPoint.x - _startPoint.x);;
}

- (CGFloat)ySpeedAtT:(CGFloat)t {
    return 2 * (_startPoint.y + _endPoint.y - 2 * _controlPoint.y) * t + 2 * (_controlPoint.y - _startPoint.y);
}

- (CGFloat)xAtT:(CGFloat)t {
    CGFloat x = pow((1-t), 2) * _startPoint.x + 2 * (1-t)* t * _controlPoint.x + pow(t, 2) * _endPoint.x;
    return x;
}

- (CGFloat)yAtT:(CGFloat)t {
    CGFloat y = pow((1-t), 2) * _startPoint.y + 2 * (1-t) * t * _controlPoint.y + pow(t, 2) * _endPoint.y;
    return y;
}

接下里就就开始矫正间隔了

//矫正间隔
- (CGFloat)uniformSpeedAtT:(CGFloat)t {
    CGFloat totalLength = [self lengthWithT:1.0];
    CGFloat len = t*totalLength; 
    CGFloat t1=t, t2;
    do {
        t2 = t1 -([self lengthWithT:t1] - len)/[self speedAtT:t1];
        if(fabs(t1-t2)<0.001) break;
        t1=t2;
    }while(true);
    return t2;
}
矫正间隔的取点

加上颜色,就是这样了


均匀的渐变色点

接下来就是要去足够多的点来连成曲线了,这个取点的个数要根据具体情况来定,
考虑到线的边界问题,处理起来太费事了,要进行更多的色值计算


曲线的边界

我的做法是先按线段长度取点,然后再根据每个点的速度及方向进行上下左右偏移,得到一条宽度足够的线之后在进行mask裁剪。

偏移得到足够宽的线

最终效果

这个取点的方案还不是很完善,先放个demo吧至少效果是出来了,可以看一下具体的样子。


下面的文章给了我很多的帮助,有需要的同学可以去看一下
匀速贝塞尔曲线运动(续)
一分钟就懂贝塞尔曲线
Bezier - 匀速贝塞尔曲线运动的实现
动态绘制贝塞尔曲线的在线演示

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容