当前位置:操作系统 > 安卓/Android >>

Android 吸入动画效果详解

1,背景

 
上图演示了动画的某几帧,其中从1 - 4,演示了图片从原始图形吸入到一个点(红色标识)。

实现这样的效果,我们利用了Canvas.drawBitmapMesh()方法,这里涉及到了一个Mesh的概念。

 

2,Mesh的概念

 


Mesh表示网格,说得通俗一点,可以将画板想像成一张格子布,在这个张布上绘制图片。对于一个网格端点均匀分布的网格来说,横向有meshWidth + 1个顶点,纵向有meshHeight + 1个端点。顶点数据verts是以行优先的数组(二维数组以一维数组表示,先行后列)。网格可以不均匀分布。  

 

 

 

上图中显示了把图片分成很多格子,上图中的每个格子是均匀的,它的顶点数是:(meshWidth + 1) * (meshHeight + 1)个,那么放这些顶点的一维数据的大小应该是:(meshWidth + 1) * (meshHeight + 1) * 2 (一个点包含x, y坐标)

    float[] vertices = new float[:(meshWidth + 1) * (meshHeight + 1) * 2];

试想,我们让这个格子(mesh)不均匀分布,那么绘制出来的图片就会变形,  

 

 

 

3,如何构建Mesh

 


吸入动画的核心是吸入到一个点,那么我们就是要在不同的时刻构造出不同的mesh的顶点坐标,我们是怎么做的呢?

 


3.1,创建两条路径(Path)

假如我们的吸入效果是从上到下吸入,  

 

 

上图中蓝色的线表示我们构造的Path,其实只要我们沿着这两条Path来构造mesh顶点就可以了。


构建两条Path的代码如下:

 

 

[java]
mFirstPathMeasure.setPath(mFirstPath, false); 
mSecondPathMeasure.setPath(mSecondPath, false); 
 
float w = mBmpWidth; 
float h = mBmpHeight; 
 
mFirstPath.reset(); 
mSecondPath.reset(); 
mFirstPath.moveTo(0, 0); 
mSecondPath.moveTo(w, 0); 
 
mFirstPath.lineTo(0, h); 
mSecondPath.lineTo(w, h); 
 
mFirstPath.quadTo(0, (endY + h) / 2, endX, endY); 
mSecondPath.quadTo(w, (endY + h) / 2, endX, endY); 

mFirstPathMeasure.setPath(mFirstPath, false);
mSecondPathMeasure.setPath(mSecondPath, false);

float w = mBmpWidth;
float h = mBmpHeight;

mFirstPath.reset();
mSecondPath.reset();
mFirstPath.moveTo(0, 0);
mSecondPath.moveTo(w, 0);

mFirstPath.lineTo(0, h);
mSecondPath.lineTo(w, h);

mFirstPath.quadTo(0, (endY + h) / 2, endX, endY);
mSecondPath.quadTo(w, (endY + h) / 2, endX, endY);


3.2,根据Path来计算顶点坐标
算法:


1,假如我们把格子分为WIDTH, HEIGHT份,把Path的长度分的20份,[0, length],表示20个时刻。

2,第0时间,我们要的形状是一个矩形,第1时刻可能是梯形,第n时间可能是一个三角形。下图说明了动画过程中图片的变化。

 

 

 

 


3,第一条(左)Path的长度为len1,第二条(右)Path的长度为len2,对于任意时刻 t [0 - 20],我们可以知道梯形的四个顶点距Path最顶端的length。

    左上角:t * (len1 / 20)
    左下角:t * (len1 / 20) + bitmapHeight
    右上角:t * (len2 / 20)
    右下角:t * (len2 / 20) + bitmapHeight

 

 

 

 

 

我们可以通过PathMeasure类根据length算出在Path上面点的坐标,也就是说,根据两条Path,我们可以分别算了四个顶点的坐标,我这里分别叫做A, B, C, D(顺时针方向),有了点的坐标,我们可以算出AD,BC的长度,并且将基进行HEIGHT等分(因为我们把mesh分成宽WIDTH,高HEIGHT等分),将AD,BC上面每等分的点连接起来形成一条直接,将再这条直接水平WIDTH等分,根据直线方程,依据x算出y,从而算出每一个顶点的坐标。(请参考上图)

下面是计算顶点坐标的详细代码:

 

 

[java]
private void buildMeshByPathOnVertical(int timeIndex) 

    mFirstPathMeasure.setPath(mFirstPath, false); 
    mSecondPathMeasure.setPath(mSecondPath, false); 
 
    int index = 0; 
    float[] pos1 = {0.0f, 0.0f}; 
    float[] pos2 = {0.0f, 0.0f}; 
    float firstLen  = mFirstPathMeasure.getLength(); 
    float secondLen = mSecondPathMeasure.getLength(); 
 
    float len1 = firstLen / HEIGHT; 
    float len2 = secondLen / HEIGHT; 
 
    float firstPointDist  = timeIndex * len1; 
    float secondPointDist = timeIndex * len2; 
    float height = mBmpHeight; 
 
    mFirstPathMeasure.getPosTan(firstPointDist, pos1, null); 
    mFirstPathMeasure.getPosTan(firstPointDist + height, pos2, null); 
    float x1 = pos1[0]; 
    float x2 = pos2[0]; 
    float y1 = pos1[1]; 
    float y2 = pos2[1]; 
    float FIRST_DIST  = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) ); 
    float FIRST_H = FIRST_DIST / HEIGHT; 
 
    mSecondPathMeasure.getPosTan(secondPointDist, pos1, null); 
    mSecondPathMeasure.getPosTan(secondPointDist + height, pos2, null); 
    x1 = pos1[0]; 
    x2 = pos2[0]; 
    y1 = pos1[1]; 
    y2 = pos2[1]; 
 
    float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) ); 
    float SECOND_H = SECOND_DIST / HEIGHT; 
 
    for (int y = 0; y <= HEIGHT; ++y) 
    { 
        mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null); 
        mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null); 
 
        float w = pos2[0] - pos1[0]; 
        float fx1 = pos1[0]; 
        float fx2 = pos2[0]; 
        float fy1 = pos1[1]; 
        float fy2 = pos2[1]; 
        float dy = fy2 - fy1; 
        float dx = fx2 - fx1; 
 
        for (int x = 0; x <= WIDTH; ++x) 
        { 
            // y = x * dy / dx  
            float fx = x * w / WIDTH; 
            float fy = fx * dy / dx; 
 
            mVerts[index * 2 + 0] = fx + fx1; 
            mVerts[index * 2 + 1] = fy + fy1; 
 
            index += 1; 
        } 
    } 

private void buildMeshByPathOnVertical(int timeIndex)
{
    mFirstPathMeasure.setPath(mFirstPath, false);
    mSecondPathMeasure.setPath(mSecondPath, false);

    int index = 0;
&n

补充:移动开发 , Android ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,