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)效果五之應用場景和使用方法
相關文章
- 貝塞爾曲線
- Android日常學習:OpenGL 實踐之貝塞爾曲線繪製Android
- webGL入門-四階貝塞爾曲線繪製Web
- Android-貝塞爾曲線實現水波紋動畫Android動畫
- CSS transition animation的使用(內含貝賽爾曲線詳解)CSS
- 貝塞爾曲線基礎部分
- Flutter 自定義元件之貝塞爾曲線畫波浪球Flutter元件
- 貝塞爾曲線理解與應用
- canvas 二次貝塞爾曲線quadraticCurveTo()Canvas
- canvas bezierCurveTo() 三次貝塞爾曲線Canvas
- 如何理解並應用貝塞爾曲線
- Android繪圖最終篇之大戰貝塞爾三次曲線Android繪圖
- 白話經典貝塞爾曲線及其在 Android 中的應用Android
- SVG之Path路徑詳解(二),全面解析貝塞爾曲線SVG
- 貝塞爾曲線(Bezier curve)實現節點連線
- 自定義View合輯(6)-波浪(貝塞爾曲線)View
- 貝塞爾曲線原理、推導及Matlab實現Matlab
- VBA,Shapes.AddCurve SafeArrayOfPoints:=pts 畫貝塞爾曲線
- canvas基礎[二]教你編寫貝塞爾曲線工具Canvas
- 【Flutter高階玩法】 貝塞爾曲線的表象認知Flutter
- 一個貝塞爾曲線編輯工具(2d)
- 建站四部曲之移動端篇(Android+上線)Android
- 自定義View合輯(8)-跳躍的小球(貝塞爾曲線)View
- Android教你一步一步從學習貝塞爾曲線到實現波浪進度條Android
- 使用二階貝塞爾曲線實現新增購物車動畫動畫
- 【乾貨滿滿】貝塞爾曲線(Bézier curve)——什麼神仙操作
- canva畫曲線圖效果
- 曲線救國:webpack打包優化黑科技Web優化
- Flutter 實戰 - 用貝塞爾曲線畫一個帶文字的波浪球 WidgetFlutter
- Flutter BottomAppBar 自定義路徑 + 貝塞爾曲線實現閒魚底部導航FlutterAPP
- Android效能優化之佈局優化Android優化
- 用貝塞爾曲線自己寫的一個電量顯示的控制元件控制元件
- 貝塞爾曲線的css實現——淘寶加入購物車基礎動畫CSS動畫
- Android 效能優化之記憶體優化Android優化記憶體
- Android效能優化篇之服務優化Android優化
- QPM 之懸浮窗助力效能優化優化
- Android記憶體優化之圖片優化Android記憶體優化
- Android應用優化之冷啟動優化Android優化