Android 漂浮類動效的分析與實現!
注:因部分原因,本篇主要講解動效分析的思路,不提供原始碼下載,請見諒 ... ...
上一篇只講了Canvas中的drawBitmap方法,並且還說的這個方法好像很膩害、能做出很多牛逼效果的樣子,接下來這篇文章只是為了作為上一篇文章的一個小栗子,進一步拓展大家利用drawBitmap 完成動效的思路!
好了,先上失真的不能再失真的效果圖:
--------------------------------------------------
如果你想看 GAStudio更多技術文章,請戳這裡; QQ技術交流群:277582728;--------------------------------------------------
咱們先一起來分析下上面的效果:
假定這是你剛從UE 或動效射雞溼手裡拿到上面的動效設計圖,映入眼簾的是蒼茫的星空,漂浮的星球營造出的深邃、浩瀚的宇宙,好了,不多BB了,針對上圖你會想到什麼樣的實現方案?
1. 有些同學可能會想到建立對應數量個ImageView,然後針對每一個ImageView使用 Animation或Animator 去做對應的移動效果;
2.採用自己繪製的方式進行實現,不就是漂浮的星球嗎,咱都給畫出來;
不用說,上面第二種方案肯定更可取,第一種方案有以下幾個缺陷:
1. 建立的view 個數過多,效能太差;
2. 靈活性太差,比如UE或產品要增加或減少星球數量,都會是個麻煩事兒;
3. 對每一個view做移動動畫,開銷太大還不太可控或修改;
針對於此,咱們堅定不移的走自己繪製完成漂浮動效的路線;
既然要繪製,那首先得拿到星球的點陣圖,根據星球的種類拿到所有的點陣圖:
/** * init bitmap info */ private void initBitmapInfo() { mBackBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.back)) .getBitmap(); mBackWidth = mBackBitmap.getWidth(); mBackHeight = mBackBitmap.getHeight(); mStarOne = ((BitmapDrawable) mResources.getDrawable(R.drawable.star2)) .getBitmap(); mStarOneWidth = mStarOne.getWidth(); mStarOneHeight = mStarOne.getHeight(); mStarTwo = ((BitmapDrawable) mResources.getDrawable(R.drawable.star1)) .getBitmap(); mStarTwoWidth = mStarTwo.getWidth(); mStarTwoHeight = mStarTwo.getHeight(); mStarThree = ((BitmapDrawable) mResources.getDrawable(R.drawable.star3)) .getBitmap(); mStarThreeWidth = mStarThree.getWidth(); mStarThreeHeight = mStarThree.getHeight(); }
上面拿到了背景和三種類型星球的點陣圖,根據上面的效果,我們來分析下,有哪些特徵性資料:
1. 同一種星球有大有小;
2. 彼此之間有透明度的差別;
3. 漂浮的方向不一樣;
4. 漂浮的速度不一樣;
5. 每個星球都得有自己的位置;
我們暫且只分析這麼多,基於此,我們抽象出星球物件:
/**
* 星球
* @author AJian
*/
private class StarInfo {
// 縮放比例
float sizePercent;
// x位置
int xLocation;
// y位置
int yLocation;
// 透明度
float alpha;
// 漂浮方向
int direction;
// 漂浮速度
int speed;
}
為了得到上面的部分資料,我們先寫一些資料或方法:
1. 為了初始化星球的位置,我們用陣列先定義一批星球的位置(基於view寬高的比例),當然大家也可以隨機,只是隨機可能出現扎堆情況:
private static final float[][] STAR_LOCATION = new float[][] {
{0.5f, 0.2f}, {0.68f, 0.35f}, {0.5f, 0.05f},
{0.15f, 0.15f}, {0.5f, 0.5f}, {0.15f, 0.8f},
{0.2f, 0.3f}, {0.77f, 0.4f}, {0.75f, 0.5f},
{0.8f, 0.55f}, {0.9f, 0.6f}, {0.1f, 0.7f},
{0.1f, 0.1f}, {0.7f, 0.8f}, {0.5f, 0.6f}
};
2. 獲取星球大小的方法(基於原始Bitmap縮放比例):
/**
* 獲取星球大小
*/
private float getStarSize(float start, float end) {
float nextFloat = (float) Math.random();
if (start < nextFloat && nextFloat < end) {
return nextFloat;
} else {
// 如果不處於想要的資料段,則再隨機一次,因為不斷遞迴有風險
return (float) Math.random();
}
}
3. 定義三種不同快慢的漂浮速度:
mFloatTransLowSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f,
mResources.getDisplayMetrics());
mFloatTransMidSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.75f,
mResources.getDisplayMetrics());
mFloatTransFastSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f,
mResources.getDisplayMetrics());
4. 獲取星球漂浮方向的方法:
/**
* 初始化星球執行方向
*/
private int getStarDirection() {
Random random = new Random();
int randomInt = random.nextInt(4);
int direction = 0;
switch (randomInt) {
case 0:
direction = LEFT;
break;
case 1:
direction = RIGHT;
break;
case 2:
direction = TOP;
break;
case 3:
direction = BOTTOM;
break;
default:
break;
}
return direction;
}
有了上面的資料和方法,我們首先初始化一定數量的星球資料:
/**
* 初始化星球資訊
*/
private void initStarInfo() {
StarInfo starInfo = null;
Random random = new Random();
for (int i = 0; i < mStarCount; i++) {
// 獲取星球大小比例
float starSize = getStarSize(0.4f, 0.9f);
// 初始化星球大小
float[] starLocation = STAR_LOCATION[i];
starInfo = new StarInfo();
starInfo.sizePercent = starSize;
// 初始化漂浮速度
int randomSpeed = random.nextInt(3);
switch (randomSpeed) {
case 0:
starInfo.speed = mFloatTransLowSpeed;
break;
case 1:
starInfo.speed = mFloatTransMidSpeed;
break;
case 2:
starInfo.speed = mFloatTransFastSpeed;
break;
default:
starInfo.speed = mFloatTransMidSpeed;
break;
}
// 初始化星球透明度
starInfo.alpha = getStarSize(0.3f, 0.8f);
// 初始化星球位置
starInfo.xLocation = (int) (starLocation[0] * mTotalWidth);
starInfo.yLocation = (int) (starLocation[1] * mTotalHeight);
log("xLocation = " + starInfo.xLocation + "--yLocation = "
+ starInfo.yLocation);
log("stoneSize = " + starSize + "---stoneAlpha = "
+ starInfo.alpha);
// 初始化星球位置
starInfo.direction = getStarDirection();
mStarInfos.add(starInfo);
}
}
有了這些資料,我們已經可以將星球繪製在螢幕上:
private void drawStarDynamic(int count, StarInfo starInfo,
Canvas canvas, Paint paint) {
float starAlpha = starInfo.alpha;
int xLocation = starInfo.xLocation;
int yLocation = starInfo.yLocation;
float sizePercent = starInfo.sizePercent;
xLocation = (int) (xLocation / sizePercent);
yLocation = (int) (yLocation / sizePercent);
Bitmap bitmap = null;
Rect srcRect = null;
Rect destRect = new Rect();
if (count % 3 == 0) {
bitmap = mStarOne;
srcRect = mStarOneSrcRect;
destRect.set(xLocation, yLocation,
xLocation + mStarOneWidth, yLocation
+ mStarOneHeight);
} else if (count % 2 == 0) {
bitmap = mStarThree;
srcRect = mStarThreeSrcRect;
destRect.set(xLocation, yLocation, xLocation
+ mStarThreeWidth, yLocation + mStarThreeHeight);
} else {
bitmap = mStarTwo;
srcRect = mStarTwoSrcRect;
destRect.set(xLocation, yLocation, xLocation
+ mStarTwoWidth, yLocation + mStarTwoHeight);
}
paint.setAlpha((int) (starAlpha * 255));
canvas.save();
canvas.scale(sizePercent, sizePercent);
canvas.drawBitmap(bitmap, srcRect, destRect, paint);
canvas.restore();
}
接下來要考慮的只是如何讓星球動起來,有了以上資料和思路,相信大家讓星球動起來就不是難事了,只需要根據星球運動的方向,每次重繪的時候將星球的x、y增加或減小對應大小即可:
private void resetStarFloat(StarInfo starInfo) {
switch (starInfo.direction) {
case LEFT:
starInfo.xLocation -= starInfo.speed;
break;
case RIGHT:
starInfo.xLocation += starInfo.speed;
break;
case TOP:
starInfo.yLocation -= starInfo.speed;
break;
case BOTTOM:
starInfo.yLocation += starInfo.speed;
break;
default:
break;
}
}
這時候有部分同學可能會說了,尼瑪, 星球直接移出螢幕了怎麼辦,這個問題相信大家都能解決,只是一個值的判斷和重新修復,不再多言;
最後針對這一類動效小談一下,其實很大一部分效果和上面的動效是類似的,不信?我舉幾個栗子:
1. 雪花飄落的效果
尼瑪,扯淡呢!雪花和這類似?雪花從上往下飛,並且還旋轉;
針對於此,我們只需要在抽取物件的時候加上旋轉角度和旋轉速度(對於旋轉有問題的話,可以參考一個絢麗的loading動效分析與實現!),至於從上往下飛的問題,我們只需要修改x、y的更新策略即可;
2. 很多桌面類應用的花瓣飄落、落英繽紛效果;
基本都是採用以上的分析和實現原理,重要的是對於資料的抽取和靈活運用,其他的也就是根據具體的需求動態的更新需要更新的資料,比如位置、大小、透明度等等;
所以,從上面來看,這一類效果其實並不複雜,我們所需要做的只是將複雜動效進行分解、抽取,然後找到每一個小點最適合的實現方式,大的動效化小,然後逐個擊破;
我在寫的過程中會盡可能的把思路描述清楚,因為在我看來,做動效,最主要的還是在於效果的拆解、銜接、解決的思路,思路清晰了,解決方案一般也就明瞭了;
--------------------------------------------------
如果你想看 GAStudio
Github主頁,請戳這裡;
如果你想看 GAStudio更多技術文章,請戳這裡;
QQ技術交流群:277582728;
--------------------------------------------------