Android 窗簾(Curtain)效果四之賽貝爾曲線優化
Github原始碼
下一篇:Android 窗簾(Curtain Menu)效果五之應用場景和使用方法
經過 上一篇文章的優化後我們最終實現的應用效果自我感覺已經不錯了。但是仔細再滑動了幾下又發下好幾處問題,琢磨了下發現這坑有點深啊,正弦曲線優化貌似行不通了;在排除手勢拖拽效果的前提下,我們配合如下實際的應用效果圖找出問題點:
自己實現特效 | Morning Routine |
---|---|
問題點:
(1).效果圖的最下邊好像被擠出去了,沒看到皺褶空隙(上一篇文章已經解決)
(2).左邊皺褶的波浪間距是均勻分佈的,右邊波浪間距是從左往右遞減的
這裡裡我們要對第二點進行優化,糾結了好久,看到這篇文章Android 使用貝塞爾曲線將多點連成一條平滑的曲線靈感就來了,這篇文章的要點主要是三階賽貝爾曲線的控制點計算,和PathMeasure的使用,這裡就不介紹了。我把程式碼clone下來修改了下實現了一個不均勻分佈的波浪曲線,如下圖1
綠色線條上邊的紅點是我們繪製平滑賽貝爾曲線的頂點,我通過為每個頂點之間設定不同的水平距離和動態控制頂點之間的垂直距離,使得綠色線條從直線扭曲成不均勻波浪賽貝爾曲線,這正是我想要的結果。附上這個Demo原始碼
原理先不寫,有空再加吧,下面就是我通過賽貝爾曲線對水平/豎直畫素優化畫素的結果:
完整程式碼:
public class CurtainView extends View {
private Bitmap mbitmap;
private static int WIDTH = 30;
private static int HEIGHT = 30;
//最大垂直的波形高度
private static float V_MAX_WAVE_HEIGHT = 450;
//最大垂直的波形高度
private static float H_MAX_WAVE_HEIGHT = 50;
//小格相交的總的點數
private int COUNT = (WIDTH + 1) * (HEIGHT + 1);
private float[] verts = new float[COUNT * 2];
private float[] origs = new float[COUNT * 2];
private int[] colors = new int[COUNT * 2];
private float k;
private float progress;
private int bitmapwidth;
private int bitmapheight;
private List<Point> points;
private float[] pos = new float[2];
private PathMeasure pathMeasure;
private float[] xOffsets = new float[WIDTH + 1];
private float[] yOffsets = new float[WIDTH + 1];
float[] waveTops = {0, 0.03F, 0.08F, 0.15F, 0.24F, 0.36F, 0.49F, 0.64F, 0.81F, 1.0F};
public CurtainView(Context context) {
super(context);
init();
}
public CurtainView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CurtainView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init() {
mbitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.timg);
bitmapwidth = mbitmap.getWidth();
bitmapheight = mbitmap.getHeight();
points = new ArrayList<>();
pathMeasure = new PathMeasure();
COUNT = (WIDTH + 1) * (HEIGHT + 1);
verts = new float[COUNT * 2];
origs = new float[COUNT * 2];
int index = 0;
for (int i = 0; i < HEIGHT + 1; i++) {
float fy = bitmapheight / (float) HEIGHT * i;
for (int j = 0; j < WIDTH + 1; j++) {
float fx = bitmapwidth / (float) WIDTH * j;
//偶數位記錄x座標 奇數位記錄Y座標
origs[index * 2] = verts[index * 2] = fx;
origs[index * 2 + 1] = verts[index * 2 + 1] = fy;
index++;
}
}
for (int i = 0; i < waveTops.length; i++) {
Point point = new Point();
point.x = (int) Math.floor((double) (bitmapwidth * waveTops[i]));
point.y = i % 2 == 0 ? 0 : (int) (H_MAX_WAVE_HEIGHT * progress);
points.add(point);
}
BezierUtils.measurePath(pathMeasure, points);
}
public void setProgress(float progress) {
this.progress = progress;
for (int t = 0; t < waveTops.length; t++) {
Point point = points.get(t);
point.y = t % 2 == 0 ? 0 : (int) (H_MAX_WAVE_HEIGHT * progress);
}
BezierUtils.measurePath(pathMeasure, points);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int j = 0; j < WIDTH + 1; j++) {
pathMeasure.getPosTan(pathMeasure.getLength() * j / (float) WIDTH, pos, null);
xOffsets[j] = pos[0];
yOffsets[j] = pos[1];
}
int index = 0;
for (int i = 0; i < HEIGHT + 1; i++) {
for (int j = 0; j < WIDTH + 1; j++) {
//把每一個水平畫素通過正弦公式轉換成正弦曲線
//H_MAX_WAVE_HEIGHT表示波峰跟波低的垂直距離,皺褶後會王桑超過水平線,所以往下偏移WAVE_HEIGHT / 2
//5表示波浪的密集度,表示波峰波谷總共有五個,對應上面左圖的1,2,3,4,5
//j就是水平像的X軸座標
//K決定正弦曲線起始點(x=0)點的Y座標,k=0就是從波峰波谷的中間開始左->右繪製曲線
float yOffset = H_MAX_WAVE_HEIGHT / 2 * progress + H_MAX_WAVE_HEIGHT / 2 * progress * (float) Math.sin((float) j / WIDTH * 5 * Math.PI + k);
yOffset = yOffsets[j];
//垂直方向豎直壓縮時的座標
float xPostion = origs[(i * (WIDTH + 1) + j) * 2] + (bitmapwidth - origs[(i * (WIDTH + 1) + j) * 2]) * progress;
xPostion = xOffsets[j] + (bitmapwidth - xOffsets[j]) * progress;
//垂直方向正弦曲線優化後的座標,1.1->個波峰波谷
float vXSinPostion = V_MAX_WAVE_HEIGHT / 2 * progress * (float) Math.sin((float) i / WIDTH * 1.1 * Math.PI + k);
//每個畫素扭曲後的x座標
//origs[(i*(WIDTH+1)+j)*2+0] 原圖x座標
verts[(i * (WIDTH + 1) + j) * 2] = vXSinPostion * ((bitmapwidth - xPostion) / bitmapwidth) + xPostion;
//每個畫素扭曲後的Y座標
//origs[(i*(WIDTH+1)+j)*2+1] 原圖y座標
verts[(i * (WIDTH + 1) + j) * 2 + 1] = origs[(i * (WIDTH + 1) + j) * 2 + 1] + yOffset;//
int channel = 255 - (int) (yOffset * 3);
channel = channel < 0 ? 0 : channel;
channel = channel > 255 ? 255 : channel;
colors[index] = 0xFF000000 | channel << 16 | channel << 8 | channel;
index += 1;
}
}
canvas.drawBitmapMesh(mbitmap, WIDTH, HEIGHT, verts, 0, colors, 0, null);
}
}
下一篇:Android 窗簾(Curtain Menu)效果五之應用場景和使用方法
相關文章
- canvas實現的賽貝爾曲線效果程式碼例項Canvas
- CSS transition animation的使用(內含貝賽爾曲線詳解)CSS
- Android 貝塞爾曲線Android
- 使用貝塞爾曲線裁圓優化tableView優化View
- 從 0 到 1Android 自定義 View(四)貝塞爾曲線AndroidView
- Android 自定義貝塞爾曲線工具Android
- webGL入門-四階貝塞爾曲線繪製Web
- Android日常學習:OpenGL 實踐之貝塞爾曲線繪製Android
- Android-貝塞爾曲線實現水波紋動畫Android動畫
- iOS開發之畫圖板(貝塞爾曲線)iOS
- 貝塞爾曲線基礎部分
- UIBezierPath貝塞爾曲線UI
- Flutter 自定義元件之貝塞爾曲線畫波浪球Flutter元件
- Android 效能優化(四)之記憶體優化實戰Android優化記憶體
- 建站四部曲之移動端篇(Android+上線)Android
- 貝塞爾曲線理解與應用
- iOS UIBezierPath 貝塞爾曲線iOSUI
- 我眼中的 效能劣化、優化的曲線優化
- 我眼中的效能劣化、優化的曲線優化
- 用canvas繪製一個曲線動畫——深入理解貝塞爾曲線Canvas動畫
- SVG之Path路徑詳解(二),全面解析貝塞爾曲線SVG
- 曲線救國:webpack打包優化黑科技Web優化
- 如何理解並應用貝塞爾曲線
- 貝塞爾曲線開發的藝術
- canvas實現高階貝塞爾曲線Canvas
- 白話經典貝塞爾曲線及其在 Android 中的應用Android
- Android繪圖最終篇之大戰貝塞爾三次曲線Android繪圖
- 貝塞爾曲線(Bezier curve)實現節點連線
- canvas bezierCurveTo() 三次貝塞爾曲線Canvas
- iOS UIBezierPath貝塞爾曲線常用方法iOSUI
- Path從懵逼到精通(2)——貝塞爾曲線
- js控制貝塞爾曲線程式碼例項JS線程
- 安卓自定義 View 進階:貝塞爾曲線安卓View
- Flutter 動畫曲線Curves 效果一覽Flutter動畫
- Android優化之佈局優化Android優化
- 自定義View合輯(6)-波浪(貝塞爾曲線)View
- canvas 二次貝塞爾曲線quadraticCurveTo()Canvas
- SVG <path> C 指令 三次貝塞爾曲線SVG