您好,登錄后才能下訂單哦!
這篇文章主要介紹“Android怎么實現吹蠟燭動畫”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Android怎么實現吹蠟燭動畫”文章能幫助大家解決問題。
一、簡介
我們來看這兩根萌萌的小蠟燭。
小蠟燭憋足氣把火焰燃起,一下被旁邊的哥們吹滅了 0^0 ,看起來還是萌氣十足的啊。看著圖大家應該能想到應該怎么實現了吧,自定義View!對了,但是具體要怎么把這個過程做好呢,跟著腳步一起來看一看吧。代碼稍微有點多,大家耐心觀看,有興趣的同學可以從我的GITHUB上clone下來,對著代碼看。
二、過程實現
蠟燭的繪制和動畫
本著面向對象的思想,很明顯這里就是兩個蠟燭嘛!既然是這樣那我們就定義一個蠟燭類具有蠟燭的基本屬性。
public abstract class ICandle { //蠟燭底部左下坐標 protected int mCurX; protected int mCurY; //蠟燭寬高 protected int mCandleWidth; protected int mCandleHeight; //蠟燭左眼坐標 protected Point mEyeLPoint; //蠟燭右眼坐標 protected Point mEyeRPoint; //蠟燭眼睛半徑 protected int mEyeRadius; //眼睛間隔距離 protected int mEyeDevide; //身體顏色 protected int mCandleColor; //是否停止動畫中 protected boolean mIsAnimStoping = false; //蠟燭芯坐標 protected Point mCandlewickPoint; public void initAnim(){ } public void stopAnim(){ } public void drawSelf(Canvas canvas){ } }
對應著這蠟燭還有代碼,那就一目了然了。可能需要解釋的應該就是下面的幾個方法了public void initAnim(), stopAnim()初始化開始和結束動畫需要的數據,小蠟燭將會實現這個方法,drawSelf(Canvas canvas)把畫布傳進來然后蠟燭自己繪制自己。
現在就是讓我們來看一看小蠟燭身體內部構造的時候了,hiahiahiahia!
不對,和蠟燭生死相隨的還有火焰呢!先來看看火焰吧,等下小蠟燭還要燃燒自己呢。+10086s
Flame
一樣先來一睹我們的富勒姆真容
好像也沒什么毛病,首先是里面的區域,就是Flame啦,外面的呢,就是Flame先生燃燒自己散發的人性之光和飄散的骨灰(手動抹眼淚)。
來看一下Flame的實現吧。我們一步步分析。
private static float CHANGE_FACTOR = 20; private Paint mPaint; private Path mPath; //左下點坐標 private int mCurX; private int mCurY; //火焰寬度 private int mWidth; //火焰高度 private int mHeight; //記錄初始高度 private int mPreHeight; //記錄初始寬度 private int mPreWidth; //火焰頂部貝塞爾曲線控制點變化參數 private int mTopXFactor; private int mTopYFactor; //用于實現火焰的抖動 private Random mRandom; //光環半徑 private int mHaloRadius; //正在燃燒 private boolean mIsFiring; //是否啟動停止動畫 private boolean mIsStopAnim = false; private boolean mFlagStop = false; private LinearGradient mLinearGradient; private RadialGradient mRadialGradient; private ValueAnimator mFlameAnimator; private ValueAnimator mHaloAnimator;
參數就是這些了,主要是我們的動畫實現過程,也就是我們的屬性動畫ValueAnimator這里還有兩個渲染類不知道大家用過沒有,LinearGradient和RadialGradient不了解的同學可以直接點我的博文了解一下。LinearGradient繪制出了火焰,RadialGradient繪制除了發散的光芒。
初始化的過程我就不寫了,大家對這代碼看吧。那主要的就是小火焰的是怎么繪制出來的呢 show the code
mPaint.setStyle(Paint.Style.FILL); mPaint.setShader(mLinearGradient); mPath.reset(); mPath.moveTo(mCurX, mCurY); mPath.quadTo(mCurX + mWidth / 2, mCurY + mHeight / 3, mCurX + mWidth, mCurY); mPath.quadTo(mCurX + mWidth / 2 + ((1 - mRandom.nextFloat()) * CHANGE_FACTOR) + mTopXFactor, mCurY - 2 * mHeight + mTopYFactor, mCurX, mCurY); canvas.drawPath(mPath, mPaint);
這就是火焰flame的繪制,可以看到這里用到了二次貝塞爾曲線的繪制,不太清楚貝塞爾曲線的同學也可以點這波浪Loading動畫(貝塞爾曲線)有簡單的介紹,當時是用在一個水波的view里面。這里的繪制是以前面那個圖里面的矩形為參照,我們再來看一下這個圖(當然是加強版hiahia)。
那為什么上面的x坐標還加了mRandom.nextFloat()) * CHANGE_FACTOR呢?你想啊,火焰不是會左右晃動嗎,利用一個隨機來控制左右擺動咯。
mFlameAnimator = ValueAnimator.ofFloat(0, 4).setDuration(4000); mFlameAnimator.setRepeatCount(ValueAnimator.INFINITE); mFlameAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float zeroToOne = (float) animation.getAnimatedValue(); if (zeroToOne >= 1.0f && zeroToOne <= 1.2f) { //火焰燃起 zeroToOne = 1.0f - 5 * (zeroToOne - 1.0f);//1-0 mHeight = (int) (mPreHeight * (1 - zeroToOne)); mIsFiring = true; } else if (zeroToOne >= 3.5f) { if (mFlagStop) { mFlameAnimator.cancel(); return; } //火焰被吹滅 zeroToOne = 2 * (zeroToOne - 3.5f);//0-2 mTopXFactor = (int) (-20 * zeroToOne); mTopYFactor = (int) (160 * zeroToOne); / mWidth = (int) (mPreWidth * (1 -zeroToOne)); mIsFiring = false; } } });
在4秒的時間內,火焰進行了一系列活動,從下面隨著燈芯移上來,不斷的改變火焰的位置,分為了兩部分,火焰燃起和火焰熄滅,從代碼中可以看到,火焰燃起時mHeight慢慢變大,然后就是有了升起的過程辣,另外一個就是火焰被吹滅的時候,因為吹滅的時候火焰的高度肯定是保持之前的值,所以不需要改變,而是用了mTopXFactor和mTopYFactor這個兩個因子來控制火焰的位置。好了,既然火焰有了,蠟炬成灰淚始干啊,生命之光也該出場了。
光圈的繪制和動畫就相對簡單了
mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(5); mPaint.setShader(mRadialGradient); canvas.drawCircle(mCurX + mWidth / 2, mCurY - mHeight / 2, mHaloRadius, mPaint); canvas.drawCircle(mCurX + mWidth / 2, mCurY - mHeight / 2, mHaloRadius + 5, mPaint); canvas.drawCircle(mCurX + mWidth / 2, mCurY - mHeight / 2, mHaloRadius - 5, mPaint);
這里改變的只有一個參數,mHaloRadius也就是光圈的半徑。但是不要忘了,其他參數同時也是在改變的呢,只不過是放在了mFlameAnimator里面。
好了介紹到這Flame的介紹完了,任重而道遠啊,寫了這么多卻還沒完結,讓我想到一某位古人說過,不是我。
還未老死,就先累死
FireCandle
這名字有點奇怪,火燭,厲害了Word哥。前面已經介紹過ICandle了,現在來看一下他的實現類,蠟燭兩兄弟之FireCandle。
初始化照例也就不說了,來看該有的變量。
private Paint mPaint; //中心X坐標 private int mCenterX; //記錄初始寬 private int mPreWidth; //記錄初始高 private int mPreHeight; //蠟燭芯旋轉角 private int mCandlewickDegrees = 0; private Flame mFlame; private boolean mIsFire = false; private boolean mIsStateOnStart = false; private boolean mIsStateOnEnd = false; private boolean mFlagStop = false; private ValueAnimator mCandlesAnimator;
命名還是挺規范的,應該一看就知道是干嘛的。
我們還是來主要看繪制和屬性動畫的配合,繪制就不看了(光速打臉)。來看動畫。
mCandlesAnimator = ValueAnimator.ofFloat(0, 4).setDuration(4000); mCandlesAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float zeroToOne = (float) animation.getAnimatedValue(); if (zeroToOne <= 1.0f) { //蠟燭芯蓄力下拉 mIsFire = true; mCandleWidth = mPreWidth + (int) (zeroToOne * 40); mCandleHeight = mPreHeight - (int) (zeroToOne * 30); mCandlewickDegrees = (int) (-60 + (180 + 60) * zeroToOne); refreshEyePosition(); } else if (zeroToOne <= 2.0f) { zeroToOne = zeroToOne - 1.0f; //蠟燭芯上擺 if (zeroToOne <= 0.2f) { zeroToOne = 1.0f - 5 * zeroToOne; mIsFire = false; mCandleWidth = mPreWidth + (int) (zeroToOne * 40); mCandleHeight = mPreHeight - (int) (zeroToOne * 30); mCandlewickDegrees = (int) (180 * zeroToOne); } else { if (mFlameStateListener != null && !mIsStateOnStart) { mFlameStateListener.flameStart(); mIsStateOnStart = true; } mCandleWidth = mPreWidth; mCandleHeight = mPreHeight; mCandlewickDegrees = 0; if (mFlagStop) { mCandlesAnimator.cancel(); } } refreshEyePosition(); } else if (zeroToOne >= 3.5f) { //蠟燭芯被吹歪 zeroToOne = 2 * (zeroToOne - 3.5f);//0-1 mCandlewickDegrees = (int) (-60 * zeroToOne); if (mFlameStateListener != null && !mIsStateOnEnd) { mFlameStateListener.flameEnd(); mIsStateOnEnd = true; } } } });
這個就過程就有點多了,但是其實一點都不復雜,,首先我們看動畫里面的小蠟燭,一開始,他來了一個變胖紅臉深蹲,所以呢mCandleWidth是變大的,mCandleHeight是變小的,后面那個燈芯隨著深蹲來了一個大角度旋轉,燈芯的如何旋轉大家也看到了,改變坐標系然后就可以了。用到了
canvas.rotate(mCandlewickDegrees, mCenterX, mCurY - mCandleHeight);這個方法。上擺過程也是一樣的,就不多說了。refreshEyePosition();這個方法是改變眼睛位置的,兩個地方都用到了所以稍微獨立出來了。注意mIsFire這個變量,沒有火焰的時候就做其他繪制,比如說紅眼睛等等。好了好了,介紹到這,小蠟燭的部分就結束了。
SecCandle
大蠟燭,帥蠟燭鎮樓,實際的繪制和小蠟燭的就差不多了,這里就不解釋了。
共同繪制View和控制器
AnimControler
這個類的功能很簡單,繪制地板部分還有就是把計算后傳過來的高度寬度賦給兩支蠟燭,然后控制兩支蠟燭各自開始動畫。
mFirCandle = new FirCandle(mRelativeX + mWidth / 6, mRelativeY + mHeight); mFirCandle.initCandle(mFirCandleWidth, mFirCandleHeight); mFirCandle.initAnim(); mSecCandle = new SecCandle(mRelativeX + mWidth / 2, mRelativeY + mHeight); mSecCandle.initCandle(mSecCandleWidth, mSecCandleHeight - 80); mSecCandle.initAnim();
***的***,就是我們的View了
CandlesAnimView
//16ms刷新Canvas mInvalidateAnimator = ValueAnimator.ofInt(0, 1).setDuration(16); mInvalidateAnimator.setRepeatCount(ValueAnimator.INFINITE); mInvalidateAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationRepeat(Animator animation) { invalidate(); } }); mInvalidateAnimator.start();
這個屬性動畫履行的任務就是快速的刷新界面,是Candle的動畫能夠及時顯示。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = measureDimension(WIDTH_DEFAULT * mDensity, widthMeasureSpec); int height = measureDimension(HEIGHT_DEFAULT *mDensity, heightMeasureSpec); setMeasuredDimension(width, height); } public int measureDimension(int defaultSize, int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = defaultSize; if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } @Override protected void onDraw(Canvas canvas) { if (!mIsInit) { initConfig(); mIsInit = true; } mAnimControler.drawMyView(canvas); }
可以看到***在view里面調用了我們的控制器,把cavas傳過去了。
***的tip:大家有沒有發現每個動畫的duration都是一樣的。
關于“Android怎么實現吹蠟燭動畫”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。