When I scanned Douyin in the subway, due to the poor network, it was very slow to load, so douyin would load a loading animation, which felt very interesting, so I analyzed it and wrote a Demo by myself to realize the effect.
rendering
Analysis of the animation
Analyzing the animation first, the initial state consists of two circular shapes that are tangent to each other. Break down the animation into two parts.
- From the blue ball on the left and the red ball on the right as the starting position, the blue ball moves to the right and the red ball moves to the left (while shrinking first, before restoring), and the part where the two balls meet becomes black.
- It’s the same as step 1, except that the balls change positions.
To draw
Mainly use Drawable animation to draw.
public class DouYinLoadingDrawable extends Drawable implements Animatable {}Copy the code
Start with three paints and three paths. To draw the pattern of two circles and overlapping parts in the animation.
private Paint leftBallPaint, rightBallPaint, coincideBallPaint;
private Path leftBallPath, rightBallPath, coincideBallPath;
Copy the code
Drawing is divided into two processes, blue ball left and blue ball right, creating enumerations to control the animation process.
public enum Direction {
LEFT,
RIGHT
}
Copy the code
Start drawing, in ondraw().
@Override
public void draw(@NonNull Canvas canvas) {
if (mCurrentDirection == Direction.LEFT) {
canvas.save();
leftBallPath.reset();
leftBallPath.addCircle(centerX - radius + translate, centerY, radius, Path.Direction.CCW);
canvas.drawPath(leftBallPath, leftBallPaint);
canvas.restore();
canvas.save();
rightBallPath.reset();
rightBallPath.addCircle(centerX + radius - translate, centerY, radius * scale, Path.Direction.CCW);
canvas.drawPath(rightBallPath, rightBallPaint);
canvas.restore();
canvas.save();
coincideBallPath.reset();
coincideBallPath.op(leftBallPath, rightBallPath, Path.Op.INTERSECT);
canvas.drawPath(coincideBallPath, coincideBallPaint);
canvas.restore();
} else if (mCurrentDirection == Direction.RIGHT) {
canvas.save();
rightBallPath.reset();
rightBallPath.addCircle(centerX - radius + translate, centerY, 20, Path.Direction.CCW);
canvas.drawPath(rightBallPath, rightBallPaint);
canvas.restore();
canvas.save();
leftBallPath.reset();
leftBallPath.addCircle(centerX + radius - translate, centerY, 20* scale, Path.Direction.CCW); canvas.drawPath(leftBallPath, leftBallPaint); canvas.restore(); canvas.save(); coincideBallPath.reset(); coincideBallPath.op(leftBallPath, rightBallPath, Path.Op.INTERSECT); canvas.drawPath(coincideBallPath, coincideBallPaint); canvas.restore(); }}Copy the code
Part of the animation code writing, mainly using ValueAnimator
- First start the left part of the animation, after the left part of the animation, the two balls exchange the left center of the circle, repeat the left part of the animation.
private void leftAnimation(a) {
// Pan animation
final ValueAnimator translateAnimator = ValueAnimator.ofFloat(0.2 * radius);
translateAnimator.setStartDelay(200);
translateAnimator.setDuration(350);
translateAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
translateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translate = (float) translateAnimator.getAnimatedValue(); invalidateSelf(); }});// Zoom animation
final ValueAnimator scaleAnimator = ValueAnimator.ofFloat(1.0.5 f.1);
scaleAnimator.setStartDelay(200);
scaleAnimator.setDuration(350);
scaleAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scale = (float) scaleAnimator.getAnimatedValue(); invalidateSelf(); }}); translateAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// After each animation, change the Direction value to reverse the animation
if (mCurrentDirection == Direction.LEFT)
mCurrentDirection = Direction.RIGHT;
else
mCurrentDirection = Direction.LEFT;
translate = 0; leftAnimation(); }}); animatorSet =new AnimatorSet();
animatorSet.playTogether(translateAnimator, scaleAnimator);
animatorSet.start();
}
Copy the code
Animation using
Just set drawable in ImageView
DouYinLoadingDrawable douYinLoadingDrawable =new DouYinLoadingDrawable();
imageView.setImageDrawable(douYinLoadingDrawable);
douYinLoadingDrawable.start();
Copy the code
Making the address
Remember to light a little star
Address of previous articles
Android animation – Fake bouquet live loading animation
Android animation – copy 58 city loading animation