Custom buffer function (buffer 10.2) and custom buffer function 10.2

Source: Internet
Author: User

Custom buffer function (buffer 10.2) and custom buffer function 10.2
Custom buffer function

In chapter 8, we added an animation to the Clock Project. It looks good, but it would be better if there is a suitable buffer function. In the world of display, when the clock pointer turns, it usually starts very slowly, and then it immediately pops up, finally buffering to the end. But how can we create a new one for every standard buffer function?

Besides+functionWithName:Besides,CAMediaTimingFunctionThere is also another constructor, one with four floating point parameters+functionWithControlPoints::::(Note that the strange syntax here does not contain the name of each specific parameter. This is legal in objective-C, but violates Apple's Guidelines for method naming, and it looks like a strange design ).

Using this method, we can create a custom buffer function to match our clock animation. To understand how to use this method, we need to know someCAMediaTimingFunctionHow it works.

Cubic besell Curve

CAMediaTimingFunctionThe main principle of a function is that it converts the input time into a proportional change between the start point and the end point. We can use a simple icon to explain that the horizontal axis represents time, and the vertical axis represents the amount of change, so the linear buffer is a simple diagonal line starting from the starting point (Figure 10.1 ).

 

Figure 10.1 linear buffer function Image

The slope of this curve represents the speed, and the change in the slope represents the acceleration. In principle, any accelerated curve can be expressed using this image,CAMediaTimingFunctionACubic besell CurveFunction, which can only generate a subset of the specified buffer function.CAKeyframeAnimationPath ).

You may recall that a cubic Bessert curve is defined by four vertices. The first and last vertices represent the starting and ending points of the curve, and the remaining two vertices are calledControl PointBecause they control the shape of the curve, the control points of the beiser curve are actually points outside the curve, that is, the curves do not have to pass through them. You can think of them as magnets that draw through their curves.

Figure 10.2 shows an example of a three-time besell buffer function

Figure 10.2 three-way besell buffer function

In fact, it is a very strange function. It accelerates first, then slows down, and finally accelerates again when it reaches the end. How can we use the standard buffer function to represent it with images?

CAMediaTimingFunctionThere is-getControlPointAtIndex:values:The method can be used to retrieve curve points. The design of this method is indeed a bit strange (maybe only Apple can answer why it does not simply returnCGPoint), But we can use it to find the point of the standard buffer function, and then useUIBezierPathAndCAShapeLayerTo draw it out.

The starting and ending points of the curve are always {0, 0} and {1, 1}. Therefore, we only need to retrieve the second and third points (Control Points) of the curve ). For specific code, see list 10.4. The image of all the standard buffer functions is shown in Figure 10.3.

Usage in listing 10.4UIBezierPathDrawCAMediaTimingFunction

 1 @interface ViewController () 2  3 @property (nonatomic, weak) IBOutlet UIView *layerView; 4  5 @end 6  7 @implementation ViewController 8  9 - (void)viewDidLoad10 {11     [super viewDidLoad];12     //create timing function13     CAMediaTimingFunction *function = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];14     //get control points15     CGPoint controlPoint1, controlPoint2;16     [function getControlPointAtIndex:1 values:(float *)&controlPoint1];17     [function getControlPointAtIndex:2 values:(float *)&controlPoint2];18     //create curve19     UIBezierPath *path = [[UIBezierPath alloc] init];20     [path moveToPoint:CGPointZero];21     [path addCurveToPoint:CGPointMake(1, 1)22             controlPoint1:controlPoint1 controlPoint2:controlPoint2];23     //scale the path up to a reasonable size for display24     [path applyTransform:CGAffineTransformMakeScale(200, 200)];25     //create shape layer26     CAShapeLayer *shapeLayer = [CAShapeLayer layer];27     shapeLayer.strokeColor = [UIColor redColor].CGColor;28     shapeLayer.fillColor = [UIColor clearColor].CGColor;29     shapeLayer.lineWidth = 4.0f;30     shapeLayer.path = path.CGPath;31     [self.layerView.layer addSublayer:shapeLayer];32     //flip geometry so that 0,0 is in the bottom-left33     self.layerView.layer.geometryFlipped = YES;34 }35 36 @end
View Code

 

Figure 10.3 StandardCAMediaTimingFunctionBuffer Curve

Then, for our buffer function with a custom clock pointer, we need to first faint, then quickly rise, and finally buffer the curve to the end. After some experiments, the final result is as follows:

[CAMediaTimingFunction functionWithControlPoints:1 :0 :0.75 :1];

If you convert it to the buffer function image, as shown in the last 10.4, if you add it to the clock program, as a result (see generation list 10.5 ).

 

Figure 10.4 custom buffer functions suitable for clock

In listing 10.5, a clock program with a custom buffer function is added.

 1 - (void)setAngle:(CGFloat)angle forHand:(UIView *)handView animated:(BOOL)animated 2 { 3     //generate transform 4     CATransform3D transform = CATransform3DMakeRotation(angle, 0, 0, 1); 5     if (animated) { 6         //create transform animation 7         CABasicAnimation *animation = [CABasicAnimation animation]; 8         animation.keyPath = @"transform"; 9         animation.fromValue = [handView.layer.presentationLayer valueForKey:@"transform"];10         animation.toValue = [NSValue valueWithCATransform3D:transform];11         animation.duration = 0.5;12         animation.delegate = self;13         animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:1 :0 :0.75 :1];14         //apply animation15         handView.layer.transform = transform;16         [handView.layer addAnimation:animation forKey:nil];17     } else {18         //set transform directly19         handView.layer.transform = transform;20     }21 }

Figure 10.5 an animation of a bounce that cannot be described using a three-time besell Curve

This effect cannot be expressed by a simple three-bysel curve, so it cannot be used.CAMediaTimingFunction. However, to achieve this effect, you can use the following methods:

  • UseCAKeyframeAnimationCreate an animation and divide it into several steps. each small step uses its own timing function (For details, refer to the following section ).
  • Use the timer to update the animation frame by frame (see Chapter 11th, "timer-based animation ").
Key Frame-based buffer

To use a key frame for bounce animation, we need to create a key frame for each significant point in the buffer curve (in this case, the key point is the peak value of each bounce ), then, the buffer function is used to connect each curve. At the same time, we also need to passkeyTimesSpecify the Time Offset of each key frame. Because the time of each bounce is reduced, the key frames are not evenly distributed.

Listing 10.6 shows the code for implementing the bullet ball animation (see Figure 10.6)

Listing 10.6 using a key frame for bullet ball Animation

@interface ViewController ()@property (nonatomic, weak) IBOutlet UIView *containerView;@property (nonatomic, strong) UIImageView *ballView;@end@implementation ViewController- (void)viewDidLoad{    [super viewDidLoad];    //add ball image view    UIImage *ballImage = [UIImage imageNamed:@"Ball.png"];    self.ballView = [[UIImageView alloc] initWithImage:ballImage];    [self.containerView addSubview:self.ballView];    //animate    [self animate];}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    //replay animation on tap    [self animate];}- (void)animate{    //reset ball to top of screen    self.ballView.center = CGPointMake(150, 32);    //create keyframe animation    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];    animation.keyPath = @"position";    animation.duration = 1.0;    animation.delegate = self;    animation.values = @[                         [NSValue valueWithCGPoint:CGPointMake(150, 32)],                         [NSValue valueWithCGPoint:CGPointMake(150, 268)],                         [NSValue valueWithCGPoint:CGPointMake(150, 140)],                         [NSValue valueWithCGPoint:CGPointMake(150, 268)],                         [NSValue valueWithCGPoint:CGPointMake(150, 220)],                         [NSValue valueWithCGPoint:CGPointMake(150, 268)],                         [NSValue valueWithCGPoint:CGPointMake(150, 250)],                         [NSValue valueWithCGPoint:CGPointMake(150, 268)]                         ];    animation.timingFunctions = @[                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn],                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut],                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn],                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut],                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn],                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut],                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn]                                  ];    animation.keyTimes = @[@0.0, @0.3, @0.5, @0.7, @0.8, @0.9, @0.95, @1.0];    //apply animation    self.ballView.layer.position = CGPointMake(150, 268);    [self.ballView.layer addAnimation:animation forKey:nil];}@end
View Code

Figure 10.6 bullet ball animation using key frames

This method is not bad, but it is relatively cumbersome to implement (because it is necessary to constantly calculate various key frames and time offsets) and it is strongly bound to the animation (because if you want to change an animation attribute, it means to recalculate all the key frames ). So how can we write a method and use a buffer function to convert any simple attribute animation into a Key Frame Animation? Let's implement it.

Process Automation

In listing 10.6, we split the Animation into several relatively large parts, and then use the buffer of Core Animation to enter and buffer the exit function to form the desired curve. But if we divide an animation into smaller parts, we can splice these curves (linear buffering) with straight lines ). To achieve automation, we need to know how to do the following two things:

  • Auto splits any property animation into multiple key frames
  • Use a mathematical function to represent elastic animation, making frames cheaper.

To solve the first problem, we need to copy the interpolation mechanism of Core Animation. This is a mechanism for passing in the start and end points, and then generating a new point between the two points at a specified time point. For a simple floating point start value, the formula is as follows (assuming that the time ranges from 0 to 1 ):

1 value = (endValue – startValue) × time + startValue;
If you want to insertCGPoint,CGColorRefOrCATransform3DFor this more complex type of value, we can simply apply this method to each independent element (that isCGPointThe x and y values in,CGColorRefIn red, blue, green, transparent value, orCATransform3DCoordinates of the independent matrix ). We also need some logic to split the value of the object before interpolation, and then re-encapsulate it into an object after interpolation, that is, we need to check the type in real time.

Once we can use code to obtain arbitrary interpolation between the starting values of an attribute animation, we can divide the animation into many independent key frames and then generate a linear key frame animation. Listing 10.7 shows the relevant code.

Note that we use 60 x Animation time (in seconds) as the number of key frames. At this time, because Core Animation renders the screen update based on 60 frames per second, so if we generate 60 key frames per second, we can ensure that the animation is smooth enough (although it is very likely that we can achieve good results with a lower frame rate ).

In this example, we only introduceCGPointType Interpolation code. However, the Code clearly shows how to scale to support other types. As an alternative to unrecognized types, we only return the first halffromValueIn the last halftoValue.

Listing 10.7 create a Key Frame Animation with the inserted Value

 1 float interpolate(float from, float to, float time) 2 { 3     return (to - from) * time + from; 4 } 5  6 - (id)interpolateFromValue:(id)fromValue toValue:(id)toValue time:(float)time 7 { 8     if ([fromValue isKindOfClass:[NSValue class]]) { 9         //get type10         const char *type = [fromValue objCType];11         if (strcmp(type, @encode(CGPoint)) == 0) {12             CGPoint from = [fromValue CGPointValue];13             CGPoint to = [toValue CGPointValue];14             CGPoint result = CGPointMake(interpolate(from.x, to.x, time), interpolate(from.y, to.y, time));15             return [NSValue valueWithCGPoint:result];16         }17     }18     //provide safe default implementation19     return (time < 0.5)? fromValue: toValue;20 }21 22 - (void)animate23 {24     //reset ball to top of screen25     self.ballView.center = CGPointMake(150, 32);26     //set up animation parameters27     NSValue *fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 32)];28     NSValue *toValue = [NSValue valueWithCGPoint:CGPointMake(150, 268)];29     CFTimeInterval duration = 1.0;30     //generate keyframes31     NSInteger numFrames = duration * 60;32     NSMutableArray *frames = [NSMutableArray array];33     for (int i = 0; i < numFrames; i++) {34         float time = 1 / (float)numFrames * i;35         [frames addObject:[self interpolateFromValue:fromValue toValue:toValue time:time]];36     }37     //create keyframe animation38     CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];39     animation.keyPath = @"position";40     animation.duration = 1.0;41     animation.delegate = self;42     animation.values = frames;43     //apply animation44     [self.ballView.layer addAnimation:animation forKey:nil];45 }
View Code

This can play a role, but the effect is not very good. So far, all we have done is a very complicated method to use linear buffer replication.CABasicAnimation. The advantage of this method is that we can control the buffer more accurately, which also means we can apply a fully customized buffer function. So what should we do?

The mathematics behind the buffer is not very simple, but fortunately we don't need to implement it one by one. Robert Penna has a Web page about the buffer function (http://www.robertpenner.com/easing), which contains links to the implementation of most universal buffer functions in multiple programming languages, including C. Here is an example of buffering into the buffer to exit the function (in fact there are many different ways to implement it ).

 

1 float quadraticEaseInOut(float t) 2 {3     return (t < 0.5)? (2 * t * t): (-2 * t * t) + (4 * t) - 1; 4 }
View Code

 

For our elastic ball, we can usebounceEaseOutFunction:

 1 float bounceEaseOut(float t) 2 { 3     if (t < 4/11.0) { 4         return (121 * t * t)/16.0; 5     } else if (t < 8/11.0) { 6         return (363/40.0 * t * t) - (99/10.0 * t) + 17/5.0; 7     } else if (t < 9/10.0) { 8         return (4356/361.0 * t * t) - (35442/1805.0 * t) + 16061/1805.0; 9     }10     return (54/5.0 * t * t) - (513/25.0 * t) + 268/25.0;11 }

 

If you modify the code in listing 10.7 To introducebounceEaseOutMethod. Our task is to only swap the buffer function. Now we can select any buffer type to create an animation (see Figure 10.8 ).

Listing 10.8 using key frames to implement custom buffering Functions

 1 - (void)animate 2 { 3     //reset ball to top of screen 4     self.ballView.center = CGPointMake(150, 32); 5     //set up animation parameters 6     NSValue *fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 32)]; 7     NSValue *toValue = [NSValue valueWithCGPoint:CGPointMake(150, 268)]; 8     CFTimeInterval duration = 1.0; 9     //generate keyframes10     NSInteger numFrames = duration * 60;11     NSMutableArray *frames = [NSMutableArray array];12     for (int i = 0; i < numFrames; i++) {13         float time = 1/(float)numFrames * i;14         //apply easing15         time = bounceEaseOut(time);16         //add keyframe17         [frames addObject:[self interpolateFromValue:fromValue toValue:toValue time:time]];18     }19     //create keyframe animation20     CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];21     animation.keyPath = @"position";22     animation.duration = 1.0;23     animation.delegate = self;24     animation.values = frames;25     //apply animation26     [self.ballView.layer addAnimation:animation forKey:nil];27 }

 

 

 

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.