ios 贝塞尔曲线 颜色填充_iOS 沿曲线线性渐变的贝塞尔曲线

iOS原生的渐变只支持线性的渐变,但有的时候我们需要沿曲线进行渐变。

先看下垂直线性渐变与沿曲线线性渐变的区别

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

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

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

怎样绘制曲线?

对于贝塞尔曲线的绘制,系统提供了一系列的方法,同时我们已可以通过公式计算出一条贝塞尔曲线。

如何保证颜色渐变?

找到曲线上的点,计算出每一个点的色值。

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

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

sum1 += [self speedAtT:(2*i+1)*dStep];

}

for(NSInteger i=1; 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吧至少效果是出来了,可以看一下具体的样子。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值