We sometimes encounter some seemingly very complex animation in the development, do not know how to start, today's article I will talk about how to use Cadisplaylink and cashapelayer to build some complex animation, hope to be in your next build animation, give you some inspiration. (Note: To be collected for study, if need to reprint please note original: Shahu submission )
In the next article, we'll build one of the following animations:
The animation is carried out in the outline of DU, similar to a hollow effect, the contour is filled with a double wave form, similar to the flow of water slowly into the container process.
Animation using Cadisplaylink to refresh, to ensure the flow of animation, the use of Cashapelayer to build the contour of the wave, and finally use the Mask property of Calayer to achieve the gradual filling process.
Introduction to Background knowledge
Before you explain the animation creation process, let's look at some of the knowledge points you'll use:
Cadisplaylink
Uibezierpath
Cashapelayer
Mask
If you already have a good understanding of these concepts, you can skip the background to introduce this section.
1, Cadisplaylink
Animations that are built in a way that are drawn will inevitably need to constantly refresh the rendered content to render smooth animations, and the cadisplaylink is like a timer that refreshes the screen every few milliseconds. Allows us to refresh the content we draw to the screen at the same frequency as the screen refresh rate.
Cadisplaylink is used in the following ways:
1234 |
   _displaylink = [cadisplaylink displaylinkwithtarget:self selector : @selector (updatecontent:)];     [_displaylink addtorunloop:[nsrunloop currentrunloop] formode:nsrunloopcommonmodes]; |
When Cadisplaylink is registered to Runloop, the screen refreshes with the Selector method that is bound to the target that it is on. Stopping the Cadisplaylink is very simple and requires only the Invalidate method to call it.
What's the difference between Nstimer and Cadisplaylink?
The iOS device's screen refreshes 60 times per second, normally cadisplaylink is called every time the screen is refreshed, the accuracy is very high, and the use of Cadisplaylink is relatively exclusive, which is suitable for constant redrawing of the UI, such as continuous drawing of the animation.
Nstimer use a wide range of applications, can do a single or cyclic processing of a task, the accuracy is lower than cadisplaylink.
2, Uibezierpath
Use the Uibezierpath class to create a vector-based path, which is the core graphics framework's encapsulation of cgpathref type data, using it to create lines or curves to build the shapes we want, The end of each line segment or curve segment is where the next segment begins. The set of lines or curves of these connections becomes subpath. The full path of a Uibezierpath object consists of one or more subpath.
The complete steps to create a complete Uibezierpath object are as follows:
Creates a Bézier path object.
Use method moveToPoint: To set the starting point of the initial line segment.
Add line or curve to define one or more subpath.
Modifies the properties of the Uibezierpath object associated with the drawing.
3, Cashapelayer
Cashapelayer is a layer subclass that is drawn by vector graphics instead of bitmap. Cashapelayer can be used to draw all the shapes represented by Cgpath, as described above, you can use Uibezierpath to create any path you want, use the properties of the Cashapelayer path with Uibezierpath to create the paths, Can show the shape we want.
This shape does not have to be closed, the layer path is not necessarily continuous, you can draw several different shapes on the Cashapelayer layer, but you only have one chance to set its path, Linewith, LineCap and other properties, If you want to set several shapes of different colors at the same time, you need to prepare a layer for each shape.
The following example code creates a simple stickman through Uibezierpath and Cashapelayer.
1234567891011121314151617181920212223242526 |
- (void)viewDidLoad {
[
super
viewDidLoad];
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(175, 100)];
[path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];
[path moveToPoint:CGPointMake(150, 125)];
[path addLineToPoint:CGPointMake(150, 175)];
[path addLineToPoint:CGPointMake(125, 225)];
[path moveToPoint:CGPointMake(150, 175)];
[path addLineToPoint:CGPointMake(175, 225)];
[path moveToPoint:CGPointMake(100, 150)];
[path addLineToPoint:CGPointMake(200, 150)];
//create shape layer
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.strokeColor = [UIColor colorWithRed:147/255.0 green:231/255.0 blue:182/255.0 alpha:1].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 5;
shapeLayer.lineJoin = kCALineJoinRound;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.path = path.CGPath;
//add it to our view
[self.view.layer addSublayer:shapeLayer];
}
|
The shapes shown are as follows:
4. Mask
Calayer has a property called mask, often referred to as the masking layer, which is itself a calayer type and has the same drawing and layout properties as other layers. It is similar to a child view, which is laid out relative to the parent layer (that is, the layer that owns the property), but it is not a normal child view. Unlike the general Sublayer,mask, which defines the visible area of the parent layer, the simple point is that the final parent view shows the pattern that is the intersection of the parent view itself and its properties mask.
The color property of the mask layer is irrelevant, what really matters is its outline, the Mask property is like a cutting machine, the parent view is cut by the mask, the intersecting part is left, and the other parts are discarded.
The real calayer of the masking layer is that the masking layer is not limited to static graphs, and any layer can be used as the Mask property, which means that masks can be generated in real time through code or even animations. This also provides support for us to implement the changes in the waves in the example.
Draw a wavy outline
We use the Uibezierpath to draw the contours of the waves, creating a wave curve at the top with the sine and cosine functions, and the curves in the right-angled coordinate system in 1 are as follows:
You can see the range class in ( -2π, 2π) and the Y value varies between [-1, 1].
In the case of a sinusoidal curve, it can be expressed as Y=asin (ωx+φ) +k, meaning that each symbol in the formula represents:
A – amplitude, which is the height of the crest.
(ωx+φ) – phase, which reflects where the variable y is located.
φ– initial phase, x=0 phase, reflected in the coordinate system is the image of the left and right movement.
The K – offset, which is reflected in the coordinate system, is the image's move up or down.
ω– angular velocity, which controls the sine period (number of vibrations per unit angle).
Using the above function, we can calculate the coordinate point of any position on the wave curve. By connecting these points with the Uibezierpath function Addlinetopoint, the path of the wavy shape is constructed. As long as we set the spacing between two adjacent points is small enough to build a smooth sine curve, the code to build a sine wave is as follows:
1234567891011121314151617181920 |
- (UIBezierPath *)createSinPath
{
UIBezierPath *wavePath = [UIBezierPath bezierPath];
CGFloat endX = 0;
for
(CGFloat x = 0; x < self.waveWidth + 1; x += 1) {
endX=x;
CGFloat y = self.maxAmplitude * sinf(360.0 / _waveWidth * (x * M_PI / 180) * self.frequency + self.phase * M_PI/ 180) + self.maxAmplitude;
if
(x == 0) {
[wavePath moveToPoint:CGPointMake(x, y)];
}
else
{
[wavePath addLineToPoint:CGPointMake(x, y)];
}
}
CGFloat endY = CGRectGetHeight(self.bounds) + 10;
[wavePath addLineToPoint:CGPointMake(endX, endY)];
[wavePath addLineToPoint:CGPointMake(0, endY)];
return
wavePath;
}
|
The results shown are as follows:
Here we set the two sine curve of the point on the horizontal axis spacing is 1, now to explain the calculation of y by the Axis x to come out of the process:
1 |
y = self.maxAmplitude * sinf(360.0 / _waveWidth * (x * M_PI / 180) * self.frequency + self.phase * M_PI/ 180) + self.maxAmplitude; |
The first self.maxamplitude represents the crest value of the curve, 360.0/_wavewidth calculates the number of degrees that the unit spacing 1pixel represents, and X * m_pi/180 represents the conversion of the horizontal axis value to an angle. Self.frequency represents angular velocity, that is, the number of fluctuations in the unit area, the size of the wave. Self.phase * M_PI/180 represents the initial phase in the above formula, through the change of the law of the initial phase, you can create the curve of the effect of the point moving up, Self.maxamplitude represents the offset, because we need to let the crest of the Wave curve show in the range of the layer, so we need to move the entire curve down the crest size unit, because Calayer uses the left-handed coordinate system, so moving down needs to add a crest size.
Let the wave curve move.
The point on the sine or cosine curve, regardless of angle, varies in the y-axis between its crest and trough. Take the unit orthogonal Cartesian coordinate system, as long as we increase the angle value regularly, then the point on the curve will change between [1,-1], we take the x=0 point of the curve as an example, the constant increase in angle, will let its Y value of the regularity of the change back and forth:
If the points on the curve can change so regularly, we can let the wave curve wave up.
To make all the points on the curve move, here we use Cadisplaylink to constantly refresh the shapes created by Uibezierpath, and the change in the curve between the two refreshes is achieved by adding the initial phase, the code is as follows:
1234567891011121314151617181920212223242526272829303132333435 |
- (void)startLoading
{
[_displayLink invalidate];
self.displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(updateWave:)];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
} - (void)updateWave:(CADisplayLink *)displayLink
{
self.phase += 8;
//逐渐累加初相
self.waveSinLayer.path = [self createSinPath].CGPath;
}
- (UIBezierPath *)createSinPath
{
UIBezierPath *wavePath = [UIBezierPath bezierPath];
CGFloat endX = 0;
for
(CGFloat x = 0; x < self.waveWidth + 1; x += 1) {
endX=x;
CGFloat y = self.maxAmplitude * sinf(360.0 / _waveWidth * (x * M_PI / 180) * self.frequency + self.phase * M_PI/ 180) + self.maxAmplitude;
if
(x == 0) {
[wavePath moveToPoint:CGPointMake(x, y)];
}
else
{
[wavePath addLineToPoint:CGPointMake(x, y)];
}
}
CGFloat endY = CGRectGetHeight(self.bounds) + 10;
[wavePath addLineToPoint:CGPointMake(endX, endY)];
[wavePath addLineToPoint:CGPointMake(0, endY)];
return wavePath;
}
|
Set the background color of the cashapelayer to light red, the wave curve will fluctuate in the bounds class of the layer, and the wave curve is as follows:
Build a hollow effect of rising waves
So far, we have used Cashapelayer, Uibezierpath, and cadisplaylink to achieve the dynamic wave effect, and the following is what we need to achieve is in the outline of DU, the process of rising water waves, the entire animation process, Will present a du cutout effect, and the water waves outside its contour will not be displayed, so the effect needs to be achieved with the Calayer mask property.
The animation footage we need is as follows:
Set the three picture positions to the same, the underlying placement animation has been shown in the underlying contour, the middle layer to achieve the cosine wave, the outermost is used to achieve the sine wave, the middle layer and the outermost image of the Mask property assigned to us to create the cosine wave and the sine wave of the cashapelayer, So after the animation began, using Cadisplaylink to constantly refresh the path of two cashapelayer to let waves up, and then use Cabasicanimation to two cashapelayer position animation, Implements a padding effect from bottom to top. The effect we want is complete:
The complete code example is here
Reference
Uibezierpath
Cashapelayer
Layer Masks
Animated Gold Partner: Cadisplaylink & Cashapelayer