0

How can I animate the center of a radial gradient as drawn in a custom CA Layer:

- (instancetype)init
{
self = [super init];
if (self) {
    [self setNeedsDisplay];
}
return self;
}

 - (void)drawInContext:(CGContextRef)ctx
{

size_t gradLocationsNum = 2;
CGFloat gradLocations[2] = {0.0f, 1.0f};
CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.5f};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum);
CGColorSpaceRelease(colorSpace);

CGPoint gradCenter= CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ;

CGContextDrawRadialGradient (ctx, gradient, gradCenter, 0, gradCenter, gradRadius, kCGGradientDrawsAfterEndLocation);


CGGradientRelease(gradient);
}

(from https://stackoverflow.com/a/26924839/668518)

Is there any way to move the center of a radial gradient drawn in this fashion?

Community
  • 1
  • 1
foobar5512
  • 2,470
  • 5
  • 36
  • 52

1 Answers1

0

You may generate a timer or rather a display link to trigger a refresh method on every frame.

When you start animating you should save the current point to some property (self.animationStartPoint) and save the target point to some property (self.animationEndPoint). Then save the 2 dates you want the animation between like self.animationStartDate = [NSDate date] and self.animationDate = [self.animationStartDate byAddingTimeInterval:animationDuration]. Now when timer or display link fires you need to only call [self setNeedsDisplay].

In draw method you now need to interpolate the two points. First we need to check where in animation time are we:

CGFloat scale = [[NSDate date] timeIntervalSince:self.animationStartDate] / [self.animationEndDate timeIntervalSince:self.animationStartDate];

While animating the scale should be between 0 and 1. If it is less than the animation is still waiting (if you use "after delay" procedure). If it is greater than the animation is over:

if(scale < 0.0) return;
else if(scale > 1.0) {
   scale = 1.0;
   // TODO: report animation completed, invalidate timer or display link
}

Then use the scale to find the current point:

CGPoint gradCenter = CGPointMake(self.animationStartPoint.x + (self.animationEndPoint.x - self.animationStartPoint.x)*scale, self.animationStartPoint.y + (self.animationEndPoint.y - self.animationStartPoint.y)*scale);
Matic Oblak
  • 16,318
  • 3
  • 24
  • 43