国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

一步步教你寫Slack的Loading動畫

2019-12-12 05:19:39
字體:
來源:轉載
供稿:網友

項目地址:https://github.com/JeasonWong/SlackLoadingView

老規矩,先上效果。

圖好大。。

說下第一眼看到這個動畫后的思路:

+兩根平行線,要用到直線方程 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)

詳細說明前先介紹下成員變量和一些初始化

成員變量

//靜止狀態 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;

初始化

 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; }

一、畫布旋轉及線條變化動畫(Canvas Rotate Line Change)

 /**  * 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); }

第一步動畫涉及到兩個動畫同時進行,所以使用了AnimatorSet,這個類很強大,可以讓N個動畫同時進行(playTogether),也可以讓N個動畫順序執行(playSequentially)。

說到這里,其實我的四個動畫就是順序進行的,但是每個動畫里又有同時進行的動畫,為了講解方便,我是監聽了onAnimationEnd來控制動畫執行順序,其實可以直接使用playSequentially。

上方動畫就干了兩件事:

1、旋轉畫布,從CANVAS_ROTATE_ANGLE + 0轉到CANVAS_ROTATE_ANGLE + 360,CANVAS_ROTATE_ANGLE是畫布初始傾斜角度

2、線條長度變化,從mEntireLineLength到-mEntireLineLength。

對應的onDraw方法:

 @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); }

是不是很機智,drawCRLC做了三件事:

1、畫布旋轉后又旋轉回來

2、畫半圓(為什么要畫半圓?不畫整個圓?這里留個思考題。)

3、畫線條

這樣動畫1就完成了。

二、畫布旋轉動畫(Canvas Rotate)

 /**  * 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的底子,那這個就太容易了,只是簡單的旋轉Canvas。

三、畫布旋轉圓圈變化動畫(Canvas Rotate Circle Change)

 /**  * 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); }

動畫3做了兩件事:

1、旋轉Canvas

2、變化Circle的Y坐標,達到往里縮的效果

四、線條變化動畫(Line Change)

 /**  * 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); }

動畫4只做了線條的變化。

這樣整個Slack的Loading動畫就完成了,是不是很簡單。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 米脂县| 玛纳斯县| 平泉县| 开原市| 界首市| 英山县| 巢湖市| 城口县| 灵宝市| 木里| 泗阳县| 江川县| 黄骅市| 府谷县| 尼勒克县| 萨迦县| 株洲市| 襄城县| 美姑县| 辽阳县| 辰溪县| 体育| 安图县| 堆龙德庆县| 资兴市| 清镇市| 白朗县| 新龙县| 鄂伦春自治旗| 龙江县| 冀州市| 布尔津县| 清涧县| 南岸区| 嘉禾县| 乌兰县| 浦东新区| 江达县| 栾城县| 安阳县| 合水县|