手摸手教你写Slack的Loading动画
2016-11-07 10:14
288 查看
手摸手教你写Slack的Loading动画
项目地址:https://github.com/JeasonWong/SlackLoadingView老规矩,先上效果。[b]说下第一眼看到这个动画后的思路:两根平行线,要用到直线方程 y=kx+b
另外两根平行线,与之前两根平行线的斜率相乘为-1,即k1*k2=-1
线条做圆周运动就是k值的不断变化
然后就是简单的线条长度变化
我相信很多人第一眼会和我有类似的思路,但是当我上了个厕所后意识到我想复杂了~说下上完厕所后的思路:不要想着线条是斜的,就是一个普通的线段,一个LineTo搞定(startX和stopX一样,仅Y不同)
线条的垂直更容易,直接Canvas翻转(转过后再转回)
整个动画的圆周运动也是Canvas翻转(转过后不转回)
线条的单度变化依然用属性动画(这是必须的。。)
动画开始前就让整个Canvas旋转
这样一来就太容易了。我把动画分成了四步:画布旋转及线条变化动画(Canvas Rotate Line Change)
画布旋转动画(Canvas Rotate)
画布旋转圆圈变化动画(Canvas Rotate Circle Change)
线条变化动画(Line Change)
详细说明前先介绍下成员变量和一些初始化成员变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | //静止状态 private final int STATUS_STILL = 0; //加载状态 private final int STATUS_LOADING = 1; //线条最大长度 private final int MAX_LINE_LENGTH = dp2px(getContext(), 120); //线条最短长度 private final int MIN_LINE_LENGTH = dp2px(getContext(), 40); //最大间隔时长 private final int MAX_DURATION = 3000; //最小间隔时长 private final int MIN_DURATION = 500; private Paint mPaint; private int[] mColors = new int[]{0xB07ECBDA, 0xB0E6A92C, 0xB0D6014D, 0xB05ABA94}; private int mWidth, mHeight; //动画间隔时长 private int mDuration = MIN_DURATION; //线条总长度 private int mEntireLineLength = MIN_LINE_LENGTH; //圆半径 private int mCircleRadius; //所有动画 private List<Animator> mAnimList = new ArrayList<>(); //Canvas起始旋转角度 private final int CANVAS_ROTATE_ANGLE = 60; //动画当前状态 private int mStatus = STATUS_STILL; //Canvas旋转角度 private int mCanvasAngle; //线条长度 private float mLineLength; //半圆Y轴位置 private float mCircleY; //第几部动画 private int mStep; |
1 2 3 4 5 6 7 8 9 10 11 12 13 | private void initView() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(mColors[0]); } private void initData() { mCanvasAngle = CANVAS_ROTATE_ANGLE; mLineLength = mEntireLineLength; mCircleRadius = mEntireLineLength / 5; mPaint.setStrokeWidth(mCircleRadius * 2); mStep = 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 3637 | /** * Animation1 * 动画1 * Canvas Rotate Line Change * 画布旋转及线条变化动画 */ private void startCRLCAnim() { Collection<Animator> animList = new ArrayList<>(); ValueAnimator canvasRotateAnim = ValueAnimator.ofInt(CANVAS_ROTATE_ANGLE + 0, CANVAS_ROTATE_ANGLE + 360); canvasRotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCanvasAngle = (int) animation.getAnimatedValue(); } }); animList.add(canvasRotateAnim); ValueAnimator lineWidthAnim = ValueAnimator.ofFloat(mEntireLineLength, -mEntireLineLength); lineWidthAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mLineLength = (float) animation.getAnimatedValue(); invalidate(); } }); animList.add(lineWidthAnim); AnimatorSet animationSet = new AnimatorSet(); animationSet.setDuration(mDuration); animationSet.playTogether(animList); animationSet.setInterpolator(new LinearInterpolator()); animationSet.addListener(new AnimatorListener() { @Override public void onAnimationEnd(Animator animation) { Log.d("@=>", "动画1结束"); if (mStatus == STATUS_LOADING) { mStep++; startCRAnim(); } } }); animationSet.start(); mAnimList.add(animationSet); } |
1 2 3 4 5 6 7 8 9 10 11 12 1314 | @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); switch (mStep % 4) { case 0: for (int i = 0; i < mColors.length; i++) { mPaint.setColor(mColors[i]); drawCRLC(canvas, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 - mLineLength, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mEntireLineLength, mPaint, mCanvasAngle + i * 90); } break; ... } } ... private void drawCRLC(Canvas canvas, float startX, float startY, float stopX, float stopY, @NonNull Paint paint, int rotate) { canvas.rotate(rotate, mWidth / 2, mHeight / 2); canvas.drawArc(new RectF(startX - mCircleRadius, startY - mCircleRadius, startX + mCircleRadius, startY + mCircleRadius), 180, 180, true, mPaint); canvas.drawLine(startX, startY, stopX, stopY, paint); canvas.drawArc(new RectF(stopX - mCircleRadius, stopY - mCircleRadius, stopX + mCircleRadius, stopY + mCircleRadius), 0, 180, true, mPaint); canvas.rotate(-rotate, mWidth / 2, mHeight / 2); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 3637 | /** * Animation2 * 动画2 * Canvas Rotate * 画布旋转动画 */ private void startCRAnim() { ValueAnimator canvasRotateAnim = ValueAnimator.ofInt(mCanvasAngle, mCanvasAngle + 180); canvasRotateAnim.setDuration(mDuration / 2); canvasRotateAnim.setInterpolator(new LinearInterpolator()); canvasRotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCanvasAngle = (int) animation.getAnimatedValue(); invalidate(); } }); canvasRotateAnim.addListener(new AnimatorListener() { @Override public void onAnimationEnd(Animator animation) { Log.d("@=>", "动画2结束"); if (mStatus == STATUS_LOADING) { mStep++; startCRCCAnim(); } } }); canvasRotateAnim.start(); mAnimList.add(canvasRotateAnim); } ... @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); switch (mStep % 4) { ... case 1: for (int i = 0; i < mColors.length; i++) { mPaint.setColor(mColors[i]); drawCR(canvas, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mEntireLineLength, mPaint, mCanvasAngle + i * 90); } break; ... } } ... private void drawCR(Canvas canvas, float x, float y, @NonNull Paint paint, int rotate) { canvas.rotate(rotate, mWidth / 2, mHeight / 2); canvas.drawCircle(x, y, mCircleRadius, paint); canvas.rotate(-rotate, mWidth / 2, mHeight / 2); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 3637 | /** * Animation3 * 动画3 * Canvas Rotate Circle Change * 画布旋转圆圈变化动画 */ private void startCRCCAnim() { Collection<Animator> animList = new ArrayList<>(); ValueAnimator canvasRotateAnim = ValueAnimator.ofInt(mCanvasAngle, mCanvasAngle + 90, mCanvasAngle + 180); canvasRotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCanvasAngle = (int) animation.getAnimatedValue(); } }); animList.add(canvasRotateAnim); ValueAnimator circleYAnim = ValueAnimator.ofFloat(mEntireLineLength, mEntireLineLength / 4, mEntireLineLength); circleYAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCircleY = (float) animation.getAnimatedValue(); invalidate(); } }); animList.add(circleYAnim); AnimatorSet animationSet = new AnimatorSet(); animationSet.setDuration(mDuration); animationSet.playTogether(animList); animationSet.setInterpolator(new LinearInterpolator()); animationSet.addListener(new AnimatorListener() { @Override public void onAnimationEnd(Animator animation) { Log.d("@=>", "动画3结束"); if (mStatus == STATUS_LOADING) { mStep++; startLCAnim(); } } }); animationSet.start(); mAnimList.add(animationSet); } ... @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); switch (mStep % 4) { ... case 2: for (int i = 0; i < mColors.length; i++) { mPaint.setColor(mColors[i]); drawCRCC(canvas, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mCircleY, mPaint, mCanvasAngle + i * 90); } break; ... } } ... private void drawCRCC(Canvas canvas, float x, float y, @NonNull Paint paint, int rotate) { canvas.rotate(rotate, mWidth / 2, mHeight / 2); canvas.drawCircle(x, y, mCircleRadius, paint); canvas.rotate(-rotate, mWidth / 2, mHeight / 2); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 3637 | /** * Animation4 * 动画4 * Line Change * 线条变化动画 */ private void startLCAnim() { ValueAnimator lineWidthAnim = ValueAnimator.ofFloat(mEntireLineLength, -mEntireLineLength); lineWidthAnim.setDuration(mDuration); lineWidthAnim.setInterpolator(new LinearInterpolator()); lineWidthAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mLineLength = (float) animation.getAnimatedValue(); invalidate(); } }); lineWidthAnim.addListener(new AnimatorListener() { @Override public void onAnimationEnd(Animator animation) { Log.d("@=>", "动画4结束"); if (mStatus == STATUS_LOADING) { mStep++; startCRLCAnim(); } } }); lineWidthAnim.start(); mAnimList.add(lineWidthAnim); } ... @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); switch (mStep % 4) { ... case 3: for (int i = 0; i < mColors.length; i++) { mPaint.setColor(mColors[i]); drawLC(canvas, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mEntireLineLength, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mLineLength, mPaint, mCanvasAngle + i * 90); } break; } } ... private void drawLC(Canvas canvas, float startX, float startY, float stopX, float stopY, @NonNull Paint paint, int rotate) { canvas.rotate(rotate, mWidth / 2, mHeight / 2); canvas.drawArc(new RectF(startX - mCircleRadius, startY - mCircleRadius, startX + mCircleRadius, startY + mCircleRadius), 0, 180, true, mPaint); canvas.drawLine(startX, startY, stopX, stopY, paint); canvas.drawArc(new RectF(stopX - mCircleRadius, stopY - mCircleRadius, stopX + mCircleRadius, stopY + mCircleRadius), 180, 180, true, mPaint); canvas.rotate(-rotate, mWidth / 2, mHeight / 2); } |
[/b]
相关文章推荐
- iOS动画进阶 - 手摸手教你写 Slack 的 Loading 动画
- iOS动画进阶 - 手摸手教你写 Slack 的 Loading 动画
- iOS动画进阶 - 手摸手教你写 Slack 的 Loading 动画
- iOS动画教你编写Slack的Loading动画进阶篇
- iOS动画进阶 - 教你写 Slack 的 Loading 动画
- iOS动画进阶 - 教你写 Slack 的 Loading 动画
- 一步步教你写Slack的Loading动画
- Android应用之——仿美团loading加载中动画
- 一个高仿微博等待动画loading——等待动画的简单实现
- [cocos2dx]自定义loading动画
- 最简单的Loading动画
- css3实现loading动画效果
- AJAX--UpdateProgress设置CSS元素POSITION的使动画居中 & loading的Info
- 一款炫酷Loading动画--加载失败
- 【Android】Android开发之常用的loading等待效果实现,仿微博等待动画。两种实现方式详解
- 一款Loading动画的实现思路(四·完结篇)
- prefix css3漏斗Loading加载动画
- android应用的loading加载动画制作
- android 应用的loading加载动画制作(闪屏)
- 各种加载效果,适合做加载loading动画效果 Eclipse版