Yahoo! News parallax animation from the definition of View

Bloggers statement:

Please reprint this article at the beginning of additional links and author information, and marked as reserved. This article by the blogger  Whiskers Meow  original audience for support and advice.

This article first appeared in this    blogger : Whiskers Meow   |   blog home page : https://blog.csdn.net/smile_running

    Today, on the phone to see such a loading effect looks pretty cool, ah turn round to several small, color is also different, it disappeared after the turn over, and then loads the contents out. Description probably the case with language, we directly see the effect of it.

    See this effect, I was thinking inside the skull, the circle it up and how to turn it round draw of the week is actually relatively simple, but to turn it up, be sure to change the coordinate values ​​occur, then exactly how make the appropriate change it, we are going step by step analysis and implementation of the results.

    First of all, to see the effect of this time, the first step we can do is put outside this small circle 6 to draw out, not much to say, let's draw a fixed six small circle is like, how to move consider.

    To draw six static small round, it is not difficult. The track is actually a small round circle arc of it, then the track must be enclosed in a great circle, so, we first inside this great circle to draw out.

    Then consider drawing small circles, since we have the great circle drawn out, certainly until the center and radius of the great circle, then each is a small round draw different positions according to different angles, so an average of about six small circle, every 60 ° intervals as the difference between the two. Well, look at the following between my sketches to clear out

    According to the above I painted that picture, we see a great circle one week a total of six small circle, we take as an example a circle, the center point P is small round green coordinates of the point we are asking for. Because our small circle 6 is the angle corresponding to the average, we also know the angle a can be calculated by a trigonometric equation x, y side length, thus obtained coordinates of the point P anyway .

    上面的效果就是这样计算的,下面我们来看看代码应该怎么写。

    private void drawCircles(Canvas canvas) {
        for (int i = 0; i < mCircleCount; i++) {
            mPaint.setColor(mColors[i]);
            double diff = mAngleDiff + mPercentAngle * i;
            float x = mCenterX + (float) Math.sin(diff) * mCenterRadius;
            float y = mCenterY - (float) Math.cos(diff) * mCenterRadius;
            canvas.drawCircle(x, y, mSmallCircleRadius, mPaint);
        }
    }

没错,代码就是这么简单就可以计算出 6 个小圆的 x , y 值,接着用 canvas 画出来就形成下面的效果了

   接下来,要想让它旋转起来的话,需要属性动画进行配合处理每一个小圆所运动时变化的角度差量值,因为每个小圆的差量值都是一样的,这个差量值相当于变化的小圆旋转一周得到的角 a 的一个差量值,拿到这个值就可以配合三角函数公式计算出每一个小圆的 x ,y 坐标了。最后,属性动画所改变的差值,通过重新绘制,就能使小圆旋转起来了。代码如下:

    private void drawCenterCircle(Canvas canvas) {
        if (mValueCirclesAnimator == null) {
            //从 0 变到 2pi 就是每一个小圆所旋转的差量
            mValueCirclesAnimator = ObjectAnimator.ofFloat(0f, 2 * (float) Math.PI);
            mValueCirclesAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mAngleDiff = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mValueCirclesAnimator.setRepeatCount(-1);
            mValueCirclesAnimator.setDuration(5000);
            mValueCirclesAnimator.setInterpolator(new LinearInterpolator());
            mValueCirclesAnimator.start();
        }
        drawCircles(canvas);
    }

小圆的旋转效果如下

    好了,到这一步,我们的小圆就可以顺利的旋转起来了。接下来再去实现汇聚的动画效果,等小圆的旋转动画结束之后呢,每个小圆都会开始汇聚到中心点,下面我们来看一张图。

   每一条粉红色的线代表着中心位置与小圆的距离,也就是大圆的半径,它们的值都是一样的。要想汇聚到中心的话,那必然要改变大圆的半径,直到半径缩为 0 ,既每一个小圆就重合了。

    所以根据上面的分析,我们在旋转动画结束以后,要对大圆的半径进行缩小,并且要重新绘制一下,代码如下:

    private void startConvergeAnimator() {
        if (mConvergeAnimator == null) {
            mConvergeAnimator = ObjectAnimator.ofFloat(mCenterRadius, 0);
            mConvergeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mCenterRadius = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mConvergeAnimator.setDuration(2000);
            mConvergeAnimator.setInterpolator(new AnticipateInterpolator(3f));
            mConvergeAnimator.start();
        }
    }

    这个动画的执行是在旋转动画之后开始的,所以要监听旋转动画的结束事件,并且结束时,取消旋转动画,代码如下:

        mRotateAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mRotateAnimator.cancel();
                //旋转动画结束后,播放汇聚动画
                startConvergeAnimator();
            }
        });

这样就会看到如下的效果:

    来到这一步,我们就剩下最后一个圆的扩散效果,圆的扩散是从汇聚之后的那个中心点开始的,首先它是一个空心的圆,随着半径慢慢的变大,然后把要显示的内容揭开,视觉效果很强。

    在这一步可能会有一点难度,因为你可能会搞混。我们的半径其实是不断的从 0 变到对角线的长度的,这毫无疑意。那么既然是空心的圆,画笔要设置为空心。这样我们就可以反过来这样想:

    比如我就绘制一个空心的圆,它的大小固定是对角线的长度,在开始时设置它的画笔宽度为对角线的长度,伴随着半径不断的增大,那么画笔宽度也就随着减小,那就会是一个圆从中间扩散的效果。看下面的草图

 黄色的线长度,既是我们要设置的画笔宽度,它的值应该是对角线减去半径。代码如下:

    private void drawSpreadCircle(Canvas canvas) {
        mPaint.setColor(mBackGround);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mDiagonal - mSpreadRadius);
        canvas.drawCircle(mCenterX, mCenterY, mDiagonal, mPaint);
    }

然后一个半径变化的属性动画代码

    private void startSpreadAnimator() {
        isSpread = true;
        if (mSpreadAnimator == null) {
            mSpreadAnimator = ObjectAnimator.ofFloat(0, mDiagonal);
            mSpreadAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mSpreadRadius = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mSpreadAnimator.setDuration(5000);
            mSpreadAnimator.start();
        }
    }

哈哈,最后的一步,我们就实现了这样的效果了。

 最后放出大招,本文效果的最终全部代码:

package nd.no.xww.qqmessagedragview;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.LinearInterpolator;

/**
 * @author xww
 * @desciption : 一个炫酷的加载动画集合效果
 * @date 2019/8/3
 * @time 17:20
 * 博主:威威喵
 * 博客:https://blog.csdn.net/smile_Running
 */
public class LoaderAnimationView extends View {

    private Paint mPaint;

    private int mWidth;
    private int mHeight;

    private float mCenterRadius;
    private int mCenterX;
    private int mCenterY;

    private float mSmallCircleRadius;

    private int mCircleCount;
    //角度差
    private float mAngleDiff = 0F;
    //每一份圆的角度占比
    private double mPercentAngle;

    //扩散半径
    private float mSpreadRadius;
    //屏幕对角线
    private float mDiagonal;

    // 旋转动画
    private ValueAnimator mRotateAnimator;
    // 汇聚动画
    private ValueAnimator mConvergeAnimator;
    // 圆的扩散动画
    private ValueAnimator mSpreadAnimator;

    private boolean isSpread = false;

    private int mBackGround;

    //6种颜色,绘制6个不同颜色的小圆
    private int[] mColors = new int[]{
            getResources().getColor(android.R.color.holo_red_dark),
            getResources().getColor(android.R.color.holo_orange_dark),
            getResources().getColor(android.R.color.holo_blue_dark),
            getResources().getColor(android.R.color.holo_green_dark),
            getResources().getColor(android.R.color.holo_purple),
            getResources().getColor(android.R.color.darker_gray)
    };

    private void init() {
        mPaint = new Paint();
        mPaint.setDither(true);
        mPaint.setAntiAlias(true);

        mBackGround = Color.parseColor("#ffffff");

        mCircleCount = mColors.length;
        //计算每一个小圆对应的角度
        mPercentAngle = 2 * Math.PI / mCircleCount;

        mWidth = getResources().getDisplayMetrics().widthPixels;
        mHeight = getResources().getDisplayMetrics().heightPixels - mWidth / 4;
        mCenterRadius = mWidth / 4;
        mSmallCircleRadius = mCenterRadius / 8;
        mCenterX = mWidth / 2;
        mCenterY = mHeight / 2;

        mDiagonal = (float) Math.sqrt(Math.pow(mCenterX, 2) + Math.pow(mCenterY, 2));
    }

    public LoaderAnimationView(Context context) {
        this(context, null);
    }

    public LoaderAnimationView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoaderAnimationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //开始旋转动画
        startRotateAnimator();
        if (!isSpread) {
            canvas.drawColor(mBackGround);
            drawCircles(canvas);
        } else {
            drawSpreadCircle(canvas);
        }
    }

    private void startRotateAnimator() {
        //从 0 变到 2pi 就是每一个小圆所旋转的差量
        if (mRotateAnimator == null) {
            mRotateAnimator = ObjectAnimator.ofFloat(0, 2 * (float) Math.PI);
            mRotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mAngleDiff = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mRotateAnimator.setDuration(3000);
            mRotateAnimator.setInterpolator(new LinearInterpolator());
            mRotateAnimator.start();

            //监听动画结束事件
            mRotateAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mRotateAnimator.cancel();
                    //旋转动画结束后,播放汇聚动画
                    startConvergeAnimator();
                }
            });
        }
    }

    private void startConvergeAnimator() {
        if (mConvergeAnimator == null) {
            mConvergeAnimator = ObjectAnimator.ofFloat(mCenterRadius, 0);
            mConvergeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mCenterRadius = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mConvergeAnimator.setDuration(2000);
            mConvergeAnimator.setInterpolator(new AnticipateInterpolator(3f));
            mConvergeAnimator.start();

            mConvergeAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mConvergeAnimator.cancel();
                    //开始扩散动画
                    startSpreadAnimator();
                }
            });
        }
    }

    private void startSpreadAnimator() {
        isSpread = true;
        if (mSpreadAnimator == null) {
            mSpreadAnimator = ObjectAnimator.ofFloat(0, mDiagonal);
            mSpreadAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mSpreadRadius = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mSpreadAnimator.setDuration(5000);
            mSpreadAnimator.start();
        }
    }

    private void drawCircles(Canvas canvas) {
        for (int i = 0; i < mCircleCount; i++) {
            mPaint.setColor(mColors[i]);
            double diff = mAngleDiff + mPercentAngle * i;
            float x = mCenterX + (float) Math.sin(diff) * mCenterRadius;
            float y = mCenterY - (float) Math.cos(diff) * mCenterRadius;
            canvas.drawCircle(x, y, mSmallCircleRadius, mPaint);
        }
    }

    private void drawSpreadCircle(Canvas canvas) {
        mPaint.setColor(mBackGround);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mDiagonal - mSpreadRadius);
        canvas.drawCircle(mCenterX, mCenterY, mDiagonal, mPaint);
    }

}

    怎么样,效果很不错吧,特地把图片换成了我的博客的头像,好喜欢这只小猫咪,一只敲可爱的小猫咪,awsl,哇,就这样吧,结束。

发布了101 篇原创文章 · 获赞 766 · 访问量 87万+

Guess you like

Origin blog.csdn.net/smile_Running/article/details/94326230