日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一款炫酷Loading动画--载入成功

發布時間:2024/4/17 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一款炫酷Loading动画--载入成功 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡單介紹

昨天在簡書上看到一篇文章。介紹了一個載入動畫的實現過程
一款Loading動畫的實現思路(一)
僅僅可惜原動畫是IOS上制作的。而看了一下。作者的實現思路比較復雜,于是趁著空暇寫了一個Android版本號。這篇文章將給大家介紹一下實現過程。


首先讓我們來看一下動畫效果



動畫結構分析

從上面的gif圖中能夠看到,這個載入動畫有成功,失敗兩種狀態,因為Gif速度比較快,我們再來分別看一張慢圖

1、成功狀態載入動畫


成功動畫的狀態轉移描寫敘述例如以下:

1、載入過程。畫藍色圓環,當進度為100%時,圓環完畢
2、從右側拋出藍色小方塊。小方塊沿著曲線到達圓環正上方
3、藍色小方塊下落。下落過程中,逐漸變長。當方塊與圓圈接觸時,進入圓環的部分變粗。同一時候圓環逐漸被擠壓,變成橢圓形
4、方塊底端到達圓環中心后,發出三個分叉向圓周延伸,同一時候橢圓被撐大。逐漸恢復回圓形
5、圓環變綠色畫出綠色勾√

整個過程能夠說是比較復雜的,甚至對照原動畫。事實上另一些細節我沒有去實現。只是接下來我為大家逐個分解每一個過程是怎么實現的。并且并不難理解

每一個小過程組合起來,就是一款炫酷動畫,希望大家都有信心去了解它。




自己定義View,依據進度繪制圓形

首先我們來實現第一個過程。圓環的繪制。
在動畫效果中。圓環的完整程度。是依據實際的進度來衡量的,當載入完畢。整個圓就畫好了。
所以我們自己定義一個View控件。在其提供了一個setProgress()方法來給使用者設置進度

public class SuperLoadingProgress extends View {/*** 當前進度*/private int progress = 0;/*** 最大進度*/private static final int maxProgress = 100;....public void setProgress(int progress) {this.progress = Math.min(progress,maxProgress);postInvalidate();if (progress==0){status = 0;}}...}

有了這個進度以后,我們就調用postInvalidate()去讓控件重繪,事實上就是觸發了其ondraw()方法。然后我們就再ondraw()方法里面。繪制圓弧
對于圓弧的繪制。相信大家都不會陌生(陌生也沒有關系。因為非常easy),僅僅要調用一個canvas.drawArc()方法就能夠了。
可是我要細致觀察這里的圓形效果。在單獨來看三張圖

圓弧起始狀態

圓弧運動狀態

圓弧終于狀態

能夠看到,首先圓弧有一定的起始角度。我們知道。在Android坐標系中,0度事實上是指水平向右開始的
也就是起點的起始角度。事實上是-90度終點的起始角度,事實上-150度

而整個過程中。
起點:-90度,逆時針旋轉270度。最后回到0度位置
終點:-150度。與起點相差60度。最后相差360度,與起點重合

所以當progress=1。也就是動畫完畢時。起點會減去270度,那么相應每一個progress
起點的位置應該是

-90-270*progress

當progress=1,終點和起點相差360度。而一開始就相差60度,所以整個過程就是多相差了300度,那么相應每一個progress。終點和起點應該相差

-(60+precent*300)

依據上面的結論。我們得到圓弧的詳細繪制方式例如以下:

/*** 起始角度*/private static final float startAngle = -90;@Overrideprotected void onDraw(Canvas canvas) {...float precent = 1.0f*progress/maxProgress;//當前完畢百分比//mRectF是代表整個view的范圍canvas.drawArc(mRectF, startAngle-270*precent, -(60 + precent*300), false, circlePaint);}



圓環完畢,拋出小方塊

在圓環繪制完畢以后,會拋出一個小方塊。小方塊沿曲線運動到圓環正上方,實際整個曲線,是一段圓弧
我們來看下圖

方塊運動狀態

運動狀態分析圖


從圖中能夠看出,方塊運動的終點,距離圓心為2R
如果運動軌跡是某個圓的一段弧,那么依據勾股定理有例如以下方程

(X+R)^2 + (2R)^2 = (X+2R)^2

解得X=R/2(事實上也非常easy解,就是勾三股四玄五)
如果我們希望方塊在500ms內從起點運動到終點。那么我們就須要提供一個計時器,告訴我們如今運動了多少毫秒。然后依據這個時間,計算出方塊當前位置
另外,因為方塊本身有一定的長度。因此方塊也有自己的起始端和末端

可是兩者的運動軌跡是一樣的,僅僅是先后不同

//拋出動畫endAngle = (float) Math.atan(4f/3);mRotateAnimation = ValueAnimator.ofFloat(0f, endAngle*0.9f );mRotateAnimation.setDuration(500);mRotateAnimation.setInterpolator(new AccelerateDecelerateInterpolator());mRotateAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {curSweepAngle = (float) animation.getAnimatedValue();//運動了多少角度invalidate();}});

每次獲得新角度。我們就去又一次繪制方塊的位置:

/*** 拋出小方塊* @param canvas*/private void drawSmallRectFly(Canvas canvas){canvas.save();canvas.translate(radius / 2 + strokeWidth, 2 * radius + strokeWidth);//將坐標移動到大圓圓心float bigRadius = 5*radius/2;//大圓半徑//方塊起始端坐標float x1 = (float) (bigRadius*Math.cos(curSweepAngle));float y1 = -(float) (bigRadius*Math.sin(curSweepAngle));//方塊末端坐標float x2 = (float) (bigRadius*Math.cos(curSweepAngle+0.05*endAngle+0.1*endAngle*(1-curSweepAngle/0.9*endAngle)));//float y2 = -(float) (bigRadius*Math.sin(curSweepAngle+0.05*endAngle+0.1*endAngle*(1-curSweepAngle/0.9*endAngle)));canvas.drawLine(x1, y1, x2, y2, smallRectPaint);//小方塊。事實上是一條直線canvas.restore(); canvas.drawArc(mRectF, 0, 360, false, circlePaint);//藍色圓環}



拋出完畢。方塊下落

能夠說下落過程,是整個動畫中最復雜的過程了。包含方塊下落。圓環擠壓,方塊變粗三個過程,整個過程,從方塊下落開始,到方塊底部究竟圓心

首先是方塊的下落,這個easy理解,方塊會逐漸變長。因為在同樣時間內,起始端和末端運動的距離不一樣
我們拿末端作為樣例,這里要使用到一個知識。就是P**ath路徑類**
這是Android提供的一個類。代表我們制定的一段路徑實例。對于方塊末端來說,其運動的路徑就是從頂部,到圓心

Path downPath1 = new Path();//起始端路徑downPath1.moveTo(2*radius+strokeWidth,strokeWidth);downPath1.lineTo(2 * radius+strokeWidth, radius+strokeWidth);Path downPath2 = new Path();//末端路徑downPath2.moveTo(2 * radius+strokeWidth, strokeWidth);downPath2.lineTo(2 * radius+strokeWidth, 2 * radius+strokeWidth);

那么問題來了,有了運動路徑以后,我們希望有動畫。起始就是希望,我們給定一個動畫時間,我們能夠獲得在這段時間的某個點上,起始端/末端運動到路徑的哪個位置
那么有了路徑以后,我們能不能獲得路徑上的隨意一個位置呢?答案是使用PathMeasure類


可能有很多朋友對這個類不熟悉,能夠參考一些文章。或者看看官方API介紹
看PathMeasure大展身手

我們首先來看,怎么初始化一個PathMeasure,非常easy,傳入一個Path對象就可以,false表示不閉合這個路徑

downPathMeasure1 = new PathMeasure(downPath1,false);downPathMeasure2 = new PathMeasure(downPath2,false);

因為動畫有一定時間。我們又須要一個計時器

//下落動畫 mDownAnimation = ValueAnimator.ofFloat(0f, 1f );mDownAnimation.setDuration(500);mDownAnimation.setInterpolator(new AccelerateInterpolator());mDownAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {downPrecent = (float) animation.getAnimatedValue();invalidate();}});

接下來是使用PathMeasure獲得下落過程中,起始端和末端的坐標

//下落方塊的起始端坐標float pos1[] = new float[2];float tan1[] = new float[2];downPathMeasure1.getPosTan(downPrecent * downPathMeasure1.getLength(), pos1, tan1);//下落方塊的末端坐標float pos2[] = new float[2];float tan2[] = new float[2];downPathMeasure2.getPosTan(downPrecent * downPathMeasure2.getLength(), pos2, tan2);

getPosTan()方法,第一個參數是指想要獲得的路徑長度。比如你設置的Path長度為100
那么你傳入60,就會獲得長度為60時的終點坐標(文字真的表達不好/(ㄒoㄒ)/~~,大家能夠去看API)

依據起始端和末端的坐標*。我們繪制一條直線。就是小方塊啦!




方塊下落,進入圓內部分變粗。圓被擠壓變形

接下來要處理一個更加復雜的問題,就是進入圓環中的方塊部分,要變粗
為了解決問題。我們就須要分辨方塊哪部分在圓內,哪部分在圓外,這個推斷起來本身就非常麻煩。況且,圓環還會被壓縮!也就是園內圓外,沒有一個固定的分界點。

怎么區分圓內圓外呢?我決定自己推斷太麻煩了,后來想到一個辦法,推斷交集!
我們知道,Android提供了API。讓我們能夠推斷兩個Rect是否相交,也能夠獲得它們的相交部分(也就是重合部分),還能夠獲得非重合部分。


如果我把方塊看成是一個矩形。圓環看成一個矩形,那么問題就簡單了,我就能夠調用API計算出進入圓內的部分,和在圓外的部分了。
例如以下圖:

我們知道,事實上圓/橢圓。都是依靠一個矩形確定的。在這個動畫中,我們希望圓被擠壓成橢圓,終于縮放比例為0.8,大概是這種

利用前面提到的計時器,我們能夠依據當前時間。知道圓被擠壓的比例。實現擠壓效果

//橢圓形區域Rect mRect = new Rect(Math.round(mRectF.left),Math.round(mRectF.top+mRectF.height()*0.1f*downPrecent),Math.round(mRectF.right),Math.round(mRectF.bottom-mRectF.height()*0.1f*downPrecent));

這樣,我們就有了代表橢圓的矩形。因為在一步中,我們知道了小方塊的起始端和末端坐標。我們能夠使這個兩個坐標,分別向左右偏移一定距離,從而獲得4個坐標。來創建矩形。
最后,我們直接利用兩個矩形,取交集和非交集,詳細實現例如以下:

//非交集Region region1 = new Region(Math.round(pos1[0])-strokeWidth/4,Math.round(pos1[1]),Math.round(pos2[0]+strokeWidth/4),Math.round(pos2[1]));region1.op(mRect, Region.Op.DIFFERENCE);drawRegion(canvas, region1, downRectPaint);//交集Region region2 = new Region(Math.round(pos1[0])-strokeWidth/2,Math.round(pos1[1]),Math.round(pos2[0]+strokeWidth/2),Math.round(pos2[1]));boolean isINTERSECT = region2.op(mRect, Region.Op.INTERSECT);drawRegion(canvas, region2, downRectPaint);

Region是Android提供的,用于處理區域運算問題的一個類,使用這個類,我們能夠非常方便進行Rect交集補集等運算,不了解的朋友,查看API

最后繪制這兩個區域,并且加上一個推斷。就是這個兩個矩形是否有相交,如果沒有,那么圓環就不用被擠壓。直接繪制圓環就可以。

//橢圓形區域if(isINTERSECT) {//如果有交集float extrusionPrecent = (pos2[1]-radius)/radius;RectF rectF = new RectF(mRectF.left, mRectF.top + mRectF.height() * 0.1f * extrusionPrecent, mRectF.right, mRectF.bottom - mRectF.height() * 0.1f * extrusionPrecent);//繪制橢圓canvas.drawArc(rectF, 0, 360, false, circlePaint);}else{canvas.drawArc(mRectF, 0, 360, false, circlePaint);//繪制圓}



下落完畢。繪制三叉

對于三叉的繪制,就沒有什么特別的了,事實上三叉就是三條Path路徑,我們用相似前面的做法,利用一個計時器,三個Path,相應三個PathMeasure,就能夠動態繪制出路徑了。

/*** 繪制分叉* @param canvas*/private void drawFork(Canvas canvas) {float pos1[] = new float[2];float tan1[] = new float[2];forkPathMeasure1.getPosTan(forkPrecent * forkPathMeasure1.getLength(), pos1, tan1);float pos2[] = new float[2];float tan2[] = new float[2];forkPathMeasure2.getPosTan(forkPrecent * forkPathMeasure2.getLength(), pos2, tan2);float pos3[] = new float[2];float tan3[] = new float[2];forkPathMeasure3.getPosTan(forkPrecent * forkPathMeasure3.getLength(), pos3, tan3);canvas.drawLine(2 * radius+strokeWidth, radius+strokeWidth, 2 * radius+strokeWidth, 2 * radius+strokeWidth, downRectPaint);canvas.drawLine(2 * radius+strokeWidth, 2 * radius+strokeWidth, pos1[0], pos1[1], downRectPaint);canvas.drawLine(2 * radius+strokeWidth, 2 * radius+strokeWidth, pos2[0], pos2[1], downRectPaint);canvas.drawLine(2 * radius+strokeWidth, 2 * radius+strokeWidth, pos3[0], pos3[1], downRectPaint);//橢圓形區域RectF rectF = new RectF(mRectF.left, mRectF.top + mRectF.height() * 0.1f * (1-forkPrecent), mRectF.right, mRectF.bottom - mRectF.height() * 0.1f * (1-forkPrecent));canvas.drawArc(rectF, 0, 360, false, circlePaint);}

最后,還要記得將橢圓還原成圓。事實上就是壓縮的逆過程
效果例如以下:



繪制綠色勾√

綠色勾的繪制事實上也和上面的做法相似,須要一個計時器,一個Path,相應的PathMeasure就可以
勾的路徑例如以下:

//初始化打鉤路徑Path tickPath = new Path();tickPath.moveTo(1.5f * radius+strokeWidth, 2 * radius+strokeWidth);tickPath.lineTo(1.5f * radius + 0.3f * radius+strokeWidth, 2 * radius + 0.3f * radius+strokeWidth);tickPath.lineTo(2*radius+0.5f * radius+strokeWidth,2*radius-0.3f * radius+strokeWidth);tickPathMeasure = new PathMeasure(tickPath,false);

最后將路徑動態繪制出現,到這里大家都非常熟悉這個做法了。可是這里我使用了另外一個方法,這種方法能夠依據進度。直接返回當前路徑成一個Path對象

/*** 繪制打鉤* @param canvas*/private void drawTick(Canvas canvas) {Path path = new Path();/** On KITKAT and earlier releases, the resulting path may not display on a hardware-accelerated Canvas. * A simple workaround is to add a single operation to this path, such as dst.rLineTo(0, 0).*/tickPathMeasure.getSegment(0, tickPrecent * tickPathMeasure.getLength(), path, true);//該方法,能夠獲得整個路徑的一部分path.rLineTo(0, 0);//解決Android本身的一個bugcanvas.drawPath(path, tickPaint);//繪制出這一部分canvas.drawArc(mRectF, 0, 360, false, tickPaint);}

于是我們在一定時間內。逐漸獲得勾這個路徑的一部分。知道獲得整個勾,并將其繪制出來!
終于效果例如以下:



寫在最后

本篇文章。首先介紹成功載入的動畫實現過程下一篇文章將會接著介紹載入失敗過程的實現。
通過這篇文章,我們應該熟悉了Path,PathMeasure,Region等一系列API,利用這些API。我們能夠方便得繪制出路徑效果。
每一個步驟組合起來,就是一個好看的,復雜的動效。對于API不熟悉的朋友,建議用到的時候去查官方文檔,或者看看其它朋友的一些介紹基礎的文章。


最后,提供源代碼下載地址和github地址,歡迎大家下載和star

轉載于:https://www.cnblogs.com/gavanwanggw/p/7253540.html

總結

以上是生活随笔為你收集整理的一款炫酷Loading动画--载入成功的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。