背景:之前偶然看到優酷有類似的頁面切換動畫效果。於是自己也打算來實現下這樣的效果。
動效說明:點擊界面中的任意位置,界面以點擊位置作爲中心點,開始以漩渦狀態,扭曲,收縮。直到消失。
直接上我實現的效果:
一,方法原理說明:
- 將頁面生成bitmap。
- 使用自定義View來繪製扭曲的圖像。 圖像繪製的時候使用的關鍵的api 是: canvas.drawBitmapMesh();
二,實現細節說明:
1. 生成頁面Bitmap: 優先使用drawingCache , 如果沒有再創建bitmap 對象。
public static Bitmap createBitmapFromView(View view) {
if (view instanceof ImageView) {
Drawable drawable = ((ImageView) view).getDrawable();
if (drawable != null && drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
}
view.clearFocus();
Bitmap bitmap = view.getDrawingCache();
if(bitmap != null) {
return bitmap;
}
bitmap = createBitmapSafely(view.getWidth(),
view.getHeight(), Bitmap.Config.ARGB_8888, 1);
if (bitmap != null) {
synchronized (sCanvas) {
Canvas canvas = sCanvas;
canvas.setBitmap(bitmap);
view.draw(canvas);
canvas.setBitmap(null);
}
}
return bitmap;
}
public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) {
...
}
2. 關於自定義控件 VortexView 。 主要是再onDraw(Canvas ) 方法中使用rootView 生成的Bitmap 通過canvas.drawBitmapMesh 方法來繪製扭曲的圖像。(最開始我的方案是支持在native 中,對圖片進行像素級別的修改。 雖然成功了,但是效率卻很慢。)
關於API drawBitmapMesh 可以參考一下這篇博文:使用drawBitmapMesh方法產生水波
期原理猜測應該是使用了opengl 中紋理,座標變換映射的技術。(只是猜測)
drawBitmapMesh使用方法:將原始圖片分割成爲M行,N列。 並計算得出原始的每個交點再二維空間內的座標。 坐上角爲(0,0)點。 水平向右爲X正方向。 垂直向下爲Y正方向。 使用漩渦算法,計算每一幀下,原始頂點(線的交點)在當前時刻下的座標位置。即生成的局部變量ve[]; 這樣界面就能顯示出圖像被扭曲的動畫。
當然:分割的行列越多,效果就會越好。
public class VortextView extends View {
...
@Override
public void onDraw(Canvas canvas) {
if (destBitmap != null) {
int mswd = mesh.getMeshWidth();
int msht = mesh.getMeshHeight();
float[] ve = mesh.getVertices();
if (rect != null) {
int count = canvas.save();
canvas.translate(rect.left, rect.top);
canvas.drawBitmapMesh(destBitmap, mswd, msht, ve, 0, null, 0, null);
canvas.restoreToCount(count);
} else {
canvas.drawBitmapMesh(destBitmap, mswd, msht, ve, 0, null, 0, null);
}
// mesh.drawLines(canvas,paint);
}
}
...
}
3. 關於算法:不管是漩渦扭曲動效,還是仿造mac os 最小化效果。 原理都是一致的。唯一不同的地方在於算法。我們需要分別設計算法來擬合出在目標時刻下新的頂點位置。
- 漩渦動效算法:這裏需要用到極座標公式。夾角隨着時間增大而增大。半徑隨着時間增大而見小。然後在求出對應的x和y;
- mac os 最小化:這裏我使用了2階貝塞爾曲線。