About the implementation of sticky animation and jelly effects in iOS

Source: Internet
Author: User

About the implementation of sticky animation and jelly effects in iOS

Recently, in a custom PageControl -- KYAnimatedPageControl, I implemented the deformation animation of CALayer and the elastic animation of CALayer. The effect was shown first:

First, make an outline:

The first topic I shared was "How To Make CALayer deformation". This technology was involved in my previous project, KYCuteView, and I wrote a short blog on implementation principles. Let's take another example today.

I have previously done elastic animations similar to jelly effects, such as this project-KYGooeyMenu. The core technology used is CAKeyframeAnimation. Then, you can set several key frames in different States to achieve this elasticity. However, after all, there are only a few key frames, and manual computation is required. If it is not accurate, the animation is not delicate enough. After all, you cannot create 60 key frames manually. Therefore, the second topic today is: "How to Create 60 key frames using the damping vibration function" to implement CALayer generation, similar to [UIView animateWithDuration: delay: usingSpringWithDamping: initialSpringVelocity: options: animations: completion.

Body.

How can CALayer be deformed?

The key technology is very simple: you need to "Spell" the Layer with multiple besell curves. The reason for doing so is self-evident, because it facilitates deformation.

For example, the ball in KYAnimatedPageControl is actually drawn as follows:

The ball is composed of four sections: arc AB, arc BC, arc CD, and arc DA. Each section of the arc is bound with two control points: the arc AB is bound with C1 and C2; arc BC is bound with C3 and C4 .....

How to express each point?

First, A, B, C, and D are four dynamic points. The variable that controls them is contentOffset. x of ScrollView. We can get this variable in real time in-(void) scrollViewDidScroll :( UIScrollView *) scrollView and convert it into a variable controlled at 0 ~ The coefficient of 1, named factor.
1

_ Factor = MIN (1, MAX (0, (ABS (scrollView. contentOffset. x-self. lastContentOffset)/scrollView. frame. size. width )));

Assume that the maximum variation distance of A, B, C, and D is 2/5 of the diameter of the ball. Then, combine this 0 ~ We can obtain the true change distance extra of A, B, C, and D as follows: extra = (self. width * 2/5) * factor. When factor = 1, the maximum deformation state is reached. The variation distance between the four points is (self. width * 2/5 ).

Note: Based on the moving direction, we also need to move point B or point D.

 
 
  1. CGPoint pointA = CGPointMake(rectCenter.x ,self.currentRect.origin.y + extra); 
  2. CGPoint pointB = CGPointMake(self.scrollDirection == ScrollDirectionLeft ? rectCenter.x + self.currentRect.size.width/2 : rectCenter.x + self.currentRect.size.width/2 + extra*2 ,rectCenter.y); 
  3. CGPoint pointC = CGPointMake(rectCenter.x ,rectCenter.y + self.currentRect.size.height/2 - extra); 
  4. CGPoint pointD = CGPointMake(self.scrollDirection == ScrollDirectionLeft ? self.currentRect.origin.x - extra*2 : self.currentRect.origin.x, rectCenter.y); 

Then there is the control point:

The key is to know the length of these horizontal and vertical dotted lines in the A-C1, B-C2, B-C3, C-C4..., named offSet. After several attempts, I came to the conclusion that:

When offSet is set to the diameter divided by 3.6, the arc can be perfectly pasted to form an arc. I vaguely felt that this 3.6 was inevitable. It seems to have something to do with 360 degrees. Maybe the inevitability of the 3.6 value can be obtained through calculation, but I didn't try it.

Therefore, the coordinates of each control point are as follows:

 
 
  1. CGPoint c1 = CGPointMake(pointA.x + offset, pointA.y); 
  2. CGPoint c2 = CGPointMake(pointB.x, pointB.y - offset); 
  3. CGPoint c3 = CGPointMake(pointB.x, pointB.y + offset); 
  4. CGPoint c4 = CGPointMake(pointC.x + offset, pointC.y); 
  5. CGPoint c5 = CGPointMake(pointC.x - offset, pointC.y); 
  6. CGPoint c6 = CGPointMake(pointD.x, pointD.y + offset); 
  7. CGPoint c7 = CGPointMake(pointD.x, pointD.y - offset); 
  8. CGPoint c8 = CGPointMake(pointA.x - offset, pointA.y); 

With the endPoint and control point, you can use the method provided in UIBezierPath-(void) addCurveToPoint :( CGPoint) endPoint controlPoint1 :( CGPoint) controlPoint1 controlPoint2 :( CGPoint) controlPoint2; draw a line segment.

Reload the-(void) drawInContext :( CGContextRef) ctx; method of CALayer, and draw a pattern in it:

 
 
  1. -(Void) drawInContext :( CGContextRef) ctx {
  2. ... // Calculate the coordinates of each point here
  3. UIBezierPath * ovalPath = [UIBezierPath bezierPath];
  4. [OvalPath moveToPoint: pointA];
  5. [OvalPath addCurveToPoint: pointB controlPoint1: c1 controlPoint2: c2];
  6. [OvalPath addCurveToPoint: pointC controlPoint1: c3 controlPoint2: c4];
  7. [OvalPath addCurveToPoint: pointD controlPoint1: c5 controlPoint2: c6];
  8. [OvalPath addCurveToPoint: pointA controlPoint1: c7 controlPoint2: c8];
  9. [OvalPath closePath];
  10. CGContextAddPath (ctx, ovalPath. CGPath );
  11. CGContextSetFillColorWithColor (ctx, self. indicatorColor. CGColor );
  12. CGContextFillPath (ctx );
  13. }

Now, when you slide the ScrollView, the ball will become deformed.

How to Use the damping vibration function to create 60 key frames?

In the preceding example, a very important factor is the contentOffset. x variable in the ScrollView. Without this input, nothing will happen next. However, to obtain this variable, the user must touch and slide to interact. In an animation, the user does not directly interact with each other. For example, when the finger leaves, the ball needs to play back the initial state with the jelly effect, and the finger has left the screen, if there is no input, the above method won't work, so we can use CAAnimation.

We know that in iOS7, Apple added a new factory method for making elastic animation in UIView (UIViewAnimationWithBlocks:

 
 
  1. + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(7_0); 

However, there is no direct CAAnimation subclass about elasticity, such as CABasicAnimation or CAKeyframeAnimation, to directly add an animation to CALayer. The good news is that the public CASpringAnimation is added to iOS9. However, for compatibility with lower versions and knowledge exploration, we can learn how to manually create an elastic animation for CALayer.

Before you start, you need to review the high school physics knowledge-damping vibration. You can click the highlighted font link to review it a little.

 

According to Wikipedia, we can get the following vibration function formula:

Of course, this is just a general expression. We need to let the image pass (0, 0) and finally fade to 1. We can first flip the original image 180 degrees around the X axis, that is, add a negative number. Then, pan up a unit along the Y axis. Therefore, the following functions can be obtained after a slight deformation:

Want to see the function image? No problem. We recommend a website -- Desmos, which allows you to view images online. In this formula, 1-\ left (e ^ {-5x} \ cdot \ cos (30x) \ right) copy and paste the post to view the image.

The improved function image is as follows:

Perfect to meet our graphics through 0, 0), shock attenuation to 1 requirements. 5 In the formula is equivalent to the damping coefficient. The smaller the value, the larger the amplitude. 30 in the formula is equivalent to the fluctuation frequency. The larger the value, the more vibrations.

Next we need to convert it into code.

The general idea is to create 60 key frames because the maximum screen refresh frequency is 60FPS), and then assign the 60 frames to the values attribute of CAKeyframeAnimation.

Use the following code to generate 60 frames and save them to an array and return them. // 1 is to create 60 values using the formula just now:

 
 
  1. + (NSMutableArray *) animationValues :( id) fromValue toValue :( id) toValue usingSpringWithDamping :( CGFloat) damping initialSpringVelocity :( CGFloat) velocity duration :( CGFloat) duration {
  2. // 60 key frames
  3. NSInteger numOfPoints = duration * 60;
  4. NSMutableArray * values = [NSMutableArray arrayWithCapacity: numOfPoints];
  5. For (NSInteger I = 0; I <numOfPoints; I ++ ){
  6. [Valueladdobject: @ (0.0)];
  7. }
  8. // Difference value
  9. CGFloat d_value = [toValue floatValue]-[fromValue floatValue];
  10. For (NSInteger point = 0; point CGFloat x = (CGFloat) point/(CGFloat) numOfPoints;
  11. CGFloat value = [toValue floatValue]-d_value * (pow (M_E,-damping * x) * cos (velocity * x )); // 1 y = 1-e ^ {-5x} * cos (30x)
  12. Values [point] = @ (value );
  13. }
  14. Return values;
  15. }

Next, create an external class method and return a CAKeyframeAnimation:

 
 
  1. +(CAKeyframeAnimation *)createSpring:(NSString *)keypath duration:(CFTimeInterval)duration usingSpringWithDamping:(CGFloat)damping initialSpringVelocity:(CGFloat)velocity fromValue:(id)fromValue toValue:(id)toValue{ 
  2. CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:keypath]; 
  3. NSMutableArray *values = [KYSpringLayerAnimation animationValues:fromValue toValue:toValue usingSpringWithDamping:damping * dampingFactor initialSpringVelocity:velocity * velocityFactor duration:duration]; 
  4. anim.values = values; 
  5. anim.duration = duration; 
  6. return anim; 

Another key

Above, we created CAKeyframeAnimation. But who are these values working? If you are familiar with CoreAnimation, that's right. It works for the passed keypath. These keypaths are actually the attribute @ property in CALayer. For example, when the input keypath is transform. rotation. x, CAKeyframeAnimation will rotate the layer, because CAKeyframeAnimation finds that CALayer has such an attribute called transform, so the animation will happen. Now we need to change the factor variable in Topic 1, so naturally we can add a property named factor to CALayer, in this way, when CAKeyframeAnimation is added to the layer, the layer will assign 60 different values to the factor. Of course, we need to control fromValue and toValue to 0 ~ 1:

 
 
  1. CAKeyframeAnimation *anim = [KYSpringLayerAnimation createSpring:@"factor" duration:0.8 usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(1) toValue:@(0)]; 
  2. self.factor = 0; 
  3. [self addAnimation:anim forKey:@"restoreAnimation"]; 

In the last step, although CAKeyframeAnimation changes the factor we want in real time, We have to notify the screen to refresh the animation.

 
 
  1. +(BOOL)needsDisplayForKey:(NSString *)key{ 
  2. if ([key isEqual:@"factor"]) { 
  3. return YES; 
  4. return [super needsDisplayForKey:key]; 

The code above notifies the screen to refresh the screen in real time when the factor changes.

Finally, you need to reload the-(id) initWithLayer :( GooeyCircle *) layer Method in CALayer. To ensure that the animation can be consistent, You need to copy the layer of the previous State and all its attributes.

 
 
  1. -(id)initWithLayer:(GooeyCircle *)layer{ 
  2. self = [super initWithLayer:layer]; 
  3. if (self) { 
  4. self.indicatorSize = layer.indicatorSize; 
  5. self.indicatorColor = layer.indicatorColor; 
  6. self.currentRect = layer.currentRect; 
  7. self.lastContentOffset = layer.lastContentOffset; 
  8. self.scrollDirection = layer.scrollDirection; 
  9. self.factor = layer.factor; 
  10. return self; 

Summary:

The most important thing for Custom Animation is to have variables and input. When sliding a ScrollView, the sliding distance is the input of the animation and can be used as the animation variable. When there is no interaction, CAAnimation can be used. In fact, there is a timer at the bottom layer of CAAnimation, And the timer function is to generate a variable, and the time is a variable. Then the variable input can be generated, and the changed state can be seen, the link is an animation.
 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.