项目需要画一个区域渐变色来体现数据的变化,于是就有了下面的示例图:
代码也不算很复杂,像素算法用的是objc,最终的使用是用swift实现的。
objc:
#import "SapCubicSpline.h"
@interface SapCubicSpline ()
@property (nonatomic, copy) NSArray *points;
@property (nonatomic, copy) NSArray *b;
@property (nonatomic, copy) NSArray *c;
@property (nonatomic, copy) NSArray *d;
@end
@implementation SapCubicSpline
- (instancetype)initWithPoints:(NSArray *)points {
if ((self = [super init])) {
self.points = points;
if (points.count > 0) {
NSUInteger count = points.count;
NSUInteger n = count; // - 1;
CGFloat x[count];
CGFloat a[count];
CGFloat h[count];
CGFloat y[count];
CGFloat l[count];
CGFloat u[count];
CGFloat z[count];
CGFloat k[count];
CGFloat s[count];
for (NSUInteger i = 0; i < points.count; i++) {
CGPoint point = [points[i] CGPointValue];
x[i] = point.x;
a[i] = point.y;
}
for (NSUInteger i = 0; i < n; i++) {
h[i] = x[i + 1] - x[i];
k[i] = a[i + 1] - a[i];
s[i] = k[i] / h[i];
}
for (NSUInteger i = 1; i < n; i++) {
y[i] = 3 / h[i] * (a[i + 1] - a[i]) - 3 / h[i - 1] * (a[i] - a[i - 1]);
}
l[0] = 1;
u[0] = 0;
z[0] = 0;
for (NSUInteger i = 1; i < n; i++) {
l[i] = 2 * (x[i + 1] - x[i - 1]) - h[i - 1] * u[i - 1];
u[i] = h[i] / l[i];
z[i] = (y[i] - h[i - 1] * z[i - 1]) / l[i];
}
l[n] = 1;
z[n] = 0;
NSMutableArray *b = [[NSMutableArray alloc] initWithCapacity:n];
NSMutableArray *c = [[NSMutableArray alloc] initWithCapacity:n];
NSMutableArray *d = [[NSMutableArray alloc] initWithCapacity:n];
for (NSUInteger i = 0; i <= n; i++) { b[i] = @0; c[i] = @0; d[i] = @0; } for (NSInteger i = n - 1; i >= 0; i--) {
c[i] = @(z[i] - u[i] * [c[i + 1] floatValue]);
b[i] = @((a[i + 1] - a[i]) / h[i] - h[i] * ([c[i + 1] floatValue] + 2.0f * [c[i] floatValue]) / 3.0f);
d[i] = @(([c[i + 1] floatValue] - [c[i] floatValue]) / (3 * h[i]));
}
c[n] = @0;
self.b = b;
self.c = c;
self.d = d;
}
}
return self;
}
- (CGFloat)interpolate:(CGFloat)input {
if (self.points.count == 0) {
// No points. Return identity.
return input;
}
CGFloat x[self.points.count];
CGFloat a[self.points.count];
for (NSUInteger i = 0; i < self.points.count; i++) { CGPoint point = [self.points[i] CGPointValue]; x[i] = point.x; a[i] = point.y; } NSInteger i = 0; for (i = self.points.count - 1; i > 0; i--) {
if (x[i] <= input) {
break;
}
}
CGFloat deltaX = input - (CGFloat)x[i];
return a[i] + [self.b[i] floatValue] * deltaX + [self.c[i] floatValue] * pow(deltaX, 2) + [self.d[i] floatValue] * pow(deltaX, 3);
}
@end
swift:
private func drawBezierLine() {
let yValues: Array<Float> = [20.0, 30.0, 25.0, 45.0, 10.0]
let chartView = UIView(frame: CGRectMake(100, 100, 200, 50))
let drawingHeight = chartView.frame.height
let drawingWidth = chartView.frame.width
var points = [CGPoint]()
for var i = 0; i < yValues.count; i++ {
let point = CGPoint(x: CGFloat(i) / CGFloat(yValues.count - 1), y: (drawingHeight - CGFloat(yValues[i])) / drawingHeight)
points.append(point)
}
let spline = SapCubicSpline(points: points.map{p in NSValue(CGPoint: p)})
let graphSize = CGSizeMake(drawingWidth, drawingHeight)
let path = CGPathCreateMutable()
for x in 0 ..< Int(graphSize.width) {
let y = spline.interpolate(CGFloat(x) / graphSize.width) * graphSize.height
if x == 0 {
CGPathMoveToPoint(path, nil, CGFloat(x), CGFloat(y))
} else {
CGPathAddQuadCurveToPoint(path, nil, CGFloat(x - 1), CGFloat(y - 1), CGFloat(x), CGFloat(y))
}
}
let lineLayer = CAShapeLayer()
lineLayer.frame = chartView.bounds
lineLayer.lineCap = kCALineCapRound
lineLayer.lineJoin = kCALineJoinRound
lineLayer.path = path
lineLayer.strokeColor = UIColor.blueColor().CGColor
lineLayer.fillColor = nil
lineLayer.lineWidth = 5.0
lineLayer.lineJoin = kCALineJoinRound
chartView.layer.addSublayer(lineLayer)
let gradientLayer = CAGradientLayer()
gradientLayer.frame = chartView.bounds
gradientLayer.colors = [UIColor.blueColor().CGColor, UIColor.orangeColor().CGColor, UIColor.purpleColor().CGColor]
gradientLayer.startPoint = CGPointMake(0, 0)
gradientLayer.endPoint = CGPointMake(1, 0)
chartView.layer.addSublayer(lineLayer)
chartView.layer.addSublayer(gradientLayer)
gradientLayer.mask = lineLayer
///area
let pathArea = CGPathCreateMutable()
for x in 0 ..< Int(graphSize.width) {
let y = spline.interpolate(CGFloat(x) / graphSize.width) * graphSize.height
CGPathMoveToPoint(pathArea, nil, CGFloat(x), CGFloat(y))
CGPathAddLineToPoint(pathArea, nil, CGFloat(x), drawingHeight)
}
let areaLayer = CAShapeLayer()
areaLayer.frame = chartView.bounds
areaLayer.lineCap = kCALineCapRound
areaLayer.lineJoin = kCALineJoinRound
areaLayer.path = pathArea
areaLayer.strokeColor = UIColor.blueColor().colorWithAlphaComponent(0.5).CGColor
areaLayer.fillColor = nil
areaLayer.lineWidth = 5.0
areaLayer.lineJoin = kCALineJoinRound
chartView.layer.addSublayer(areaLayer)
let gradientAreaLayer = CAGradientLayer()
gradientAreaLayer.frame = chartView.bounds
gradientAreaLayer.colors = [UIColor.blueColor().CGColor, UIColor.orangeColor().CGColor, UIColor.purpleColor().CGColor]
gradientAreaLayer.startPoint = CGPointMake(0, 0)
gradientAreaLayer.endPoint = CGPointMake(1, 0)
chartView.layer.addSublayer(areaLayer)
chartView.layer.addSublayer(gradientAreaLayer)
gradientAreaLayer.mask = areaLayer
self.view.addSubview(chartView)
}
原文链接:iOS渐变区域和渐变线,转载请注明来源!