preface

There is an interesting NumberMorphView animation that was mentioned earlier in iOS developer dry stuff # 1.




NumberMorphView


I’m going to do some analysis of the open source library in a few articles. Of course, this article will not be a full analysis of the library, but a few simple examples using some of the library’s technical concepts, as a primer, and I’ll write another code analysis of the library later.

What to do

We will use CADisplayLink + CAShapeLayer + UIBezierPath to create a circle drawing animation at millisecond level. The difference is that this animation has an elastic effect. Let’s take a look at the effect.




Results the preview


start

The preparatory work

  1. Create a new Single View Application project and add RRCircleAnimationView, which inherits from UIView.
  2. Open main. storyboard and change the view Custom class of the only ViewController to RRCircleAnimationView. At this point, the preparatory work has been completed.

Start drawing a circle

Let’s start with a simple task. Let’s animate the circle.

First, add attributes to the RRCircleAnimationView:

@implementation RRCircleAnimationView { CADisplayLink *_displayLink; // CADisplayLink ensures smooth animation by ensuring that our methods are called as the system renders each frame. UIBezierPath *_path; // Create a vector-based path CGPoint _beginPoint; // Start touching CGPoint _endPoint; CAShapeLayer *_shapeLayer; // Can be combined with UIBezierPath to draw}Copy the code

We then initialize the instance variable, and since we’re loading it in storyboard, we can initialize it in the awakeFromNib method

// Notice that we are loading the current view directly from the XIb. - (void)awakeFromNib { _shapeLayer = [CAShapeLayer layer]; [self.layer addSublayer:_shapeLayer]; _shapelayer.fillcolor = [UIColor colorWithRed:0.400 green:0.400 blue:1.000 alpha:1.000]. _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateFrame)]; [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; }Copy the code

Next we implement the updateFrame method CADisplayLink is calling all the time above, and we keep drawing circles inside it.

- (void)updateFrame {// Draw circle _path = [UIBezierPath bezierPathWithArcCenter:_beginPoint RADIUS :[self getRadius] startAngle:0 endAngle:M_PI*2 clockwise:YES]; _shapeLayer.path = _path.CGPath; }Copy the code

Above, we use the position of the starting point as the center of the circle, and then draw a circle according to a specific radius, which is calculated according to the starting point and ending point of our touch, and the distance from the beginning point to the end point is the radius of the circle.

Let’s find the start and end points of the touch:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    _beginPoint = point;
    _endPoint = point;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    _endPoint = point;
}Copy the code

The final calculation using our middle school mathematical knowledge, according to the two point coordinate distance formula




Two point coordinate distance formula


The distance between our starting and ending points, which is the radius of the circle, is:

- (CGFloat)getRadius { CGFloat result = sqrt(pow(_endPoint.x - _beginPoint.x, 2) + pow(_endPoint.y - _beginPoint.y, 2));  return result; }Copy the code

I’m going to draw my circle animation here.

Adding elastic effect

The above is just drawing round animation seems to be no problem, but the total feeling is lack of movement, next we will help him to add some vitality!

  1. Add a member variable to the RRCircleAnimationView class.

    BOOL _isTouchEnd; // Touch end flag int _currentFrame; // The current frame numberCopy the code
  2. Create – (void)touchesBegan:(NSSet *) Touches withEvent:(UIEvent *) Event

    _isTouchEnd = NO; // Reset touch state _currentFrame = 1; // Reset the current frame countCopy the code
  3. Add the following methods:

    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { _isTouchEnd = YES; // End of touch, update touch state}Copy the code
  4. Change the method – (CGFloat)getRadius as follows:

    - (CGFloat)getRadius { CGFloat result = sqrt(pow(_endPoint.x-_beginPoint.x, 2)+pow(_endPoint.y-_beginPoint.y, 2)); If (_isTouchEnd) {CGFloat animationDuration = 1.0; Int maxFrames = animationDuration / _displayLink. Duration; _currentFrame++; if (_currentFrame <= maxFrames)="" {="" CGFloat="" factor="[self" getSpringInterpolation:(CGFloat)(_currentFrame)="" (CGFloat)(maxFrames)]; = "" according to the formula to calculate the spring factor =" "return =" MAX_RADIUS = "" +" = "" (result =" - "=" "MAX_RADIUS) = =" "" "* factor; }else="" MAX_RADIUS; ="" }="" result; ="" }<="" code=""/>Copy the code
  5. Finally, add the magic formula:

    - (CGFloat)getSpringInterpolation:(CGFloat)x {CGFloat tension = 0.3; // Tension coefficient return pow(2, -10 * x) * sin((x-tension / 4) * (2 * M_PI)/tension); }Copy the code

The formula, expressed in mathematical notation, is:




Math


You can use Mac OS X’s built-in software called Grapher to draw an image of this function, as shown below:




Grapher


So what this function does is it takes the x value, which is the current number of frames divided by the maximum number of frames allowed.

(CGFloat)(_currentFrame) / (CGFloat)(maxFrames)Copy the code

So the range of x values is 0, 1.

The animation we want is to stretch the circle beyond or below the target radius MAX_RADIUS, and we need an elastic animation to gradually return to the target radius.

Go back to the formula for calculating the animation radius in real time:

MAX_RADIUS + (result - MAX_RADIUS) * factorCopy the code

In order for x to be equal to 1, the radius is equal to MAX_RADIUS, so factor should be equal to 0, which is f(1) = 0.

If you look at the graph of the function, it vibrates between x equals 0 and 1, and the amplitude decreases as x increases, and when x equals 1, y equals 0.

The last

This article describes how to achieve elastic frame animation, if you can understand the principle of animation production, animation effect development is very helpful, later I will continue to write some other animation production methods, to achieve more animation effects.

I almost forgot to mention that this animation is currently on Github, portal: RRongAnimation




RRongAnimation


The End