Github源碼
Android 窗簾(Curtain)效果三之波浪式扭曲效果優(yōu)化提升
上一篇文章已經(jīng)實(shí)現(xiàn)了如何把一張圖片扭曲成波浪效果,那么這一篇文章我們介紹如何動(dòng)態(tài)調(diào)整系數(shù)梨树,去改變波浪圖片的皺褶成度票顾。我們自一次觀察下圖morning routine的效果:
仔細(xì)觀察我們發(fā)現(xiàn)卜范,當(dāng)往右滑動(dòng)時(shí)隧土,頭部的溝壑(也就是正弦曲線)是從0遞增到最大的;如果具體到一條水平像素直線的話藕甩,這其實(shí)就是一條直線扭曲成正弦曲線的一個(gè)過程施敢。直線如何扭曲成正弦曲線、正弦曲線如何恢復(fù)成直線其實(shí)就是系數(shù)WAVE_HEIGHT(波峰波谷距離)從0遞增到MAX_WAVE_HEIGHT、MAX_WAVE_HEIGHT遞減0的動(dòng)態(tài)變化過程僵娃,因此我們只要在前一篇文章正弦曲線的公式代碼加入滑動(dòng)的滑動(dòng)百分比progress(1>=progress>=0)就可以了:
float yOffset = WAVE_HEIGHT / 2 * progress + WAVE_HEIGHT / 2 * progress * (float) Math.sin((float) j / WIDTH * 5 * Math.PI + k);
上面已經(jīng)介紹了動(dòng)態(tài)滑動(dòng)扭曲時(shí)如何計(jì)算每條水平直線上每個(gè)像素的y軸偏移量羡藐,但是根據(jù)上圖效果其實(shí)每個(gè)像素的x坐標(biāo)也是變化的;折疊菜單的最左邊的像素點(diǎn)x坐標(biāo)等于菜單向左滑動(dòng)的距離悯许,折疊菜單的最右邊的像素點(diǎn)x坐標(biāo)緊緊的擠壓邊緣不移動(dòng),所以當(dāng)滑動(dòng)時(shí)每個(gè)像素的x坐標(biāo)是偏移量是從左往右衰減成0的辉阶,由此我們可以推導(dǎo)出滑動(dòng)時(shí)計(jì)算的每個(gè)像素x坐標(biāo)的公式:
//bitmapwidth 原圖寬度//origsX 原圖時(shí)像素的X坐標(biāo)//progress當(dāng)前滑動(dòng)百分比//xPostion 像素的新x坐標(biāo)//這個(gè)公式計(jì)算出的xPostion 越往右跟origsX 的差距越小先壕,最后一像素差距為0
float xPostion = origsX + (bitmapwidth - origsX ) * progress;
原理我們已經(jīng)理解的差不多了,那么最終的效果如下圖:
全部代碼如下:
public class CurtainView extends View {
private Bitmap mbitmap;
private static int WIDTH = 30;
private static int HEIGHT = 30;
//最大水平的波形高度
private float WAVE_HEIGHT = 50;
//小格相交的總的點(diǎn)數(shù)
private int COUNT = (WIDTH + 1) * (HEIGHT + 1);
private float[] verts = new float[COUNT * 2];
private float[] origs = new float[COUNT * 2];
private float k;
private float progress;
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 setProgress(float progress){
this.progress = progress;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < HEIGHT + 1; i++) {
for (int j = 0; j < WIDTH + 1; j++) {
//把每一個(gè)水平像素通過正弦公式轉(zhuǎn)換成正弦曲線
//WAVE_HEIGHT表示波峰跟波低的垂直距離谆甜,皺褶后會(huì)王桑超過水平線垃僚,所以往下偏移WAVE_HEIGHT / 2
//5表示波浪的密集度,表示波峰波谷總共有五個(gè),對(duì)應(yīng)上面左圖的1,2,3,4,5
//j就是水平像的X軸坐標(biāo)
//K決定正弦曲線起始點(diǎn)(x=0)點(diǎn)的Y坐標(biāo)规辱,k=0就是從波峰波谷的中間開始左->右繪制曲線
float yOffset = WAVE_HEIGHT / 2 * progress + WAVE_HEIGHT / 2 * progress * (float) Math.sin((float) j / WIDTH * 5 * Math.PI + k);
//每個(gè)像素扭曲后的x坐標(biāo)
//origs[(i*(WIDTH+1)+j)*2+0] 原圖x坐標(biāo)
verts[(i * (WIDTH + 1) + j) * 2 + 0] = origs[(i*(WIDTH+1)+j)*2+0] + (bitmapwidth - origs[(i*(WIDTH+1)+j)*2+0]) * progress;
//每個(gè)像素扭曲后的Y坐標(biāo)
//origs[(i*(WIDTH+1)+j)*2+1] 原圖y坐標(biāo)
verts[(i * (WIDTH + 1) + j) * 2 + 1] = origs[(i * (WIDTH + 1) + j) * 2 + 1] + yOffset;//
}
}
canvas.drawBitmapMesh(mbitmap, WIDTH, HEIGHT, verts, 0, null, 0, null);
}
int bitmapwidth;
int bitmapheight;
public void init() {
mbitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.timg);
bitmapwidth = mbitmap.getWidth();
bitmapheight = mbitmap.getHeight();
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;
//偶數(shù)位記錄x坐標(biāo) 奇數(shù)位記錄Y坐標(biāo)
origs[index * 2 + 0] = verts[index * 2 + 0] = fx;
origs[index * 2 + 1] = verts[index * 2 + 1] = fy;
index++;
}
}
}
}