Android 吸入动画效果详解(仿mac退出效果)

来源:互联网 发布:网络真人棋牌 编辑:程序博客网 时间:2024/04/27 13:42
转载自:http://m.blog.csdn.net/blog/leehong2005/9127095

                                                                                                                                                                                                                                                                                                                                

[转]Android 吸入动画效果详解


1,背景


吸入(Inhale)效果,最初我是在iOS上面看到的,它是在Note程序中,用户可能添加了一页记录,在做删除时,它的删除效果是:这一页内容吸入到一个垃圾框的图标里面。请看下图所示:



===============================================================================

这里,我要介绍的是如何在Android上面实现一个类似的效果。先看看我实现的效果图。




上图演示了动画的某几帧,其中从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,其实只要我们沿着这两条Path来构造mesh顶点就可以了。

构建两条Path的代码如下: 


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,从而算出每一个顶点的坐标。(请参考上图)

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


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;        }    }}


4,如何绘制


绘制代码很简单,调用Canvas.drawBitmapMesh方法。最本质是要计算出一个顶点数组。


canvas.drawBitmapMesh(mBitmap,        mInhaleMesh.getWidth(),        mInhaleMesh.getHeight(),        mInhaleMesh.getVertices(),        0, null, 0, mPaint);


5,如何实现动画

protected void applyTransformation(float interpolatedTime, Transformation t)        {            int curIndex = 0;            Interpolator interpolator = this.getInterpolator();            if (null != interpolator)            {                float value = interpolator.getInterpolation(interpolatedTime);                interpolatedTime = value;            }            if (mReverse)            {                interpolatedTime = 1.0f - interpolatedTime;            }            curIndex = (int)(mFromIndex + (mEndIndex - mFromIndex) * interpolatedTime);            if (null != mListener)            {                mListener.onAnimUpdate(curIndex);            }        }

在动画里面,我们计算出要做动画的帧的index,假设我们把吸入动画分为20帧,在动画里面,计算出每一帧,最后通过onAnimUpdate(int index)方法回调,在这个方法实现里面,我们根据帧的index来重新计算一个新的mesh顶点数组,再用这个数组来绘制bitmap。这样,我们就可以看来一组连续变化的mesh,也就能看到吸扩效果的动画。

动画类里面,最核心就是扩展Animation类,重写applyTransformation方法。


6,总结


本文简单介绍了吸放效果的实现,根据这个原理,我们可以构造更加复杂的Path来做更多的效果。同时,也能实现向上,向左,向右的吸入效果。

最本质是我们要理解Mesh的概念,最核心的工作就是构造出Mesh的顶点坐标。


计算Mesh通常是一个很复杂的工作,作一些简单的变形还可以,对于太复杂的变形,可能还是不太方便。另外,像书籍翻页的效果,用mesh其实也是可以做到的。只是算法复杂一点。

这里不能给出完整的代码,原理可能不是说得太清楚,但愿给想实现的人一个思路指引吧。


7,实现代码


InhaleAnimationActivity.java

package com.nj1s.lib.test.anim;import android.os.Bundle;import android.view.Gravity;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Button;import android.widget.LinearLayout;import com.nj1s.lib.mesh.InhaleMesh.InhaleDir;import com.nj1s.lib.test.GABaseActivity;import com.nj1s.lib.test.R;import com.nj1s.lib.test.effect.BitmapMesh;public class InhaleAnimationActivity extends GABaseActivity{    private static final boolean DEBUG_MODE = false;    private BitmapMesh.SampleView mSampleView = null;        @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);                LinearLayout linearLayout = new LinearLayout(this);                mSampleView = new BitmapMesh.SampleView(this);        mSampleView.setIsDebug(DEBUG_MODE);        mSampleView.setLayoutParams(new LinearLayout.LayoutParams(-1, -1));        Button btn = new Button(this);        btn.setText("Run");        btn.setTextSize(20.0f);        btn.setLayoutParams(new LinearLayout.LayoutParams(150, -2));        btn.setOnClickListener(new View.OnClickListener()        {            boolean mReverse = false;                        @Override            public void onClick(View v)            {                if (mSampleView.startAnimation(mReverse))                {                    mReverse = !mReverse;                }            }        });                linearLayout.setOrientation(LinearLayout.VERTICAL);        linearLayout.setGravity(Gravity.CENTER_VERTICAL);        linearLayout.addView(btn);        linearLayout.addView(mSampleView);                setContentView(linearLayout);    }    @Override    public boolean onCreateOptionsMenu(Menu menu)     {        getMenuInflater().inflate(R.menu.inhale_anim_menu, menu);        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item)    {        switch(item.getItemId())        {        case R.id.menu_inhale_down:            mSampleView.setInhaleDir(InhaleDir.DOWN);            break;                    case R.id.menu_inhale_up:            mSampleView.setInhaleDir(InhaleDir.UP);            break;                    case R.id.menu_inhale_left:            mSampleView.setInhaleDir(InhaleDir.LEFT);            break;                    case R.id.menu_inhale_right:            mSampleView.setInhaleDir(InhaleDir.RIGHT);            break;        }                return super.onOptionsItemSelected(item);    }}


BitmapMesh.java

/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.nj1s.lib.test.effect;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.Path;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.animation.Animation;import android.view.animation.Interpolator;import android.view.animation.Transformation;import com.nj1s.lib.mesh.InhaleMesh;import com.nj1s.lib.mesh.InhaleMesh.InhaleDir;import com.nj1s.lib.test.R;public class BitmapMesh {        public static class SampleView extends View {                private static final int WIDTH = 40;        private static final int HEIGHT = 40;        private final Bitmap mBitmap;        private final Matrix mMatrix = new Matrix();        private final Matrix mInverse = new Matrix();                private boolean mIsDebug = false;        private Paint mPaint = new Paint();        private float[] mInhalePt = new float[] {0, 0};        private InhaleMesh mInhaleMesh = null;        public SampleView(Context context) {            super(context);            setFocusable(true);            mBitmap = BitmapFactory.decodeResource(getResources(),                                                     R.drawable.beach);                        mInhaleMesh = new InhaleMesh(WIDTH, HEIGHT);            mInhaleMesh.setBitmapSize(mBitmap.getWidth(), mBitmap.getHeight());            mInhaleMesh.setInhaleDir(InhaleDir.DOWN);        }                public void setIsDebug(boolean isDebug)        {            mIsDebug = isDebug;        }                public void setInhaleDir(InhaleMesh.InhaleDir dir)        {            mInhaleMesh.setInhaleDir(dir);                        float w = mBitmap.getWidth();            float h = mBitmap.getHeight();            float endX = 0;            float endY = 0;            float dx = 10;            float dy = 10;            mMatrix.reset();                        switch (dir)            {            case DOWN:                endX = w / 2;                endY = getHeight() - 20;                break;                            case UP:                dy = getHeight() - h - 20;                endX = w / 2;                endY = -dy + 10;                break;                            case LEFT:                dx = getWidth() - w - 20;                endX = -dx + 10;                endY = h / 2;                break;                            case RIGHT:                endX = getWidth() - 20;                endY = h / 2;                break;            }                        mMatrix.setTranslate(dx, dy);            mMatrix.invert(mInverse);            buildPaths(endX, endY);            buildMesh(w, h);            invalidate();        }                @Override        protected void onSizeChanged(int w, int h, int oldw, int oldh)        {            super.onSizeChanged(w, h, oldw, oldh);                        float bmpW = mBitmap.getWidth();            float bmpH = mBitmap.getHeight();                        mMatrix.setTranslate(10, 10);            //mMatrix.setTranslate(10, 10);            mMatrix.invert(mInverse);                        mPaint.setColor(Color.RED);            mPaint.setStrokeWidth(2);            mPaint.setAntiAlias(true);                        buildPaths(bmpW / 2, h - 20);            buildMesh(bmpW, bmpH);        }        public boolean startAnimation(boolean reverse)        {            Animation anim = this.getAnimation();            if (null != anim && !anim.hasEnded())            {                return false;            }                        PathAnimation animation = new PathAnimation(0, HEIGHT + 1, reverse,                     new PathAnimation.IAnimationUpdateListener()            {                @Override                public void onAnimUpdate(int index)                {                    mInhaleMesh.buildMeshes(index);                    invalidate();                }            });                        if (null != animation)            {                animation.setDuration(1000);                this.startAnimation(animation);            }                        return true;        }                @Override         protected void onDraw(Canvas canvas)        {            Log.i("leehong2", "onDraw  =========== ");            canvas.drawColor(0xFFCCCCCC);            canvas.concat(mMatrix);                        canvas.drawBitmapMesh(mBitmap,                    mInhaleMesh.getWidth(),                     mInhaleMesh.getHeight(),                     mInhaleMesh.getVertices(),                    0, null, 0, mPaint);                        // ===========================================            // Draw the target point.            mPaint.setColor(Color.RED);            mPaint.setStyle(Style.FILL);            canvas.drawCircle(mInhalePt[0], mInhalePt[1], 5, mPaint);                        if (mIsDebug)            {                // ===========================================                // Draw the mesh vertices.                canvas.drawPoints(mInhaleMesh.getVertices(), mPaint);                                // ===========================================                // Draw the paths                mPaint.setColor(Color.BLUE);                mPaint.setStyle(Style.STROKE);                Path[] paths = mInhaleMesh.getPaths();                for (Path path : paths)                {                    canvas.drawPath(path, mPaint);                }            }        }                private void buildMesh(float w, float h)        {            mInhaleMesh.buildMeshes(w, h);        }                private void buildPaths(float endX, float endY)        {            mInhalePt[0] = endX;            mInhalePt[1] = endY;            mInhaleMesh.buildPaths(endX, endY);        }                int mLastWarpX = 0;        int mLastWarpY = 0;        @Override         public boolean onTouchEvent(MotionEvent event)        {            float[] pt = { event.getX(), event.getY() };            mInverse.mapPoints(pt);            if (event.getAction() == MotionEvent.ACTION_UP)            {                int x = (int)pt[0];                int y = (int)pt[1];                if (mLastWarpX != x || mLastWarpY != y) {                    mLastWarpX = x;                    mLastWarpY = y;                    buildPaths(pt[0], pt[1]);                    invalidate();                }            }            return true;        }    }        private static class PathAnimation extends Animation    {        public interface IAnimationUpdateListener        {            public void onAnimUpdate(int index);        }                private int mFromIndex = 0;        private int mEndIndex = 0;        private boolean mReverse = false;        private IAnimationUpdateListener mListener = null;                public PathAnimation(int fromIndex, int endIndex, boolean reverse, IAnimationUpdateListener listener)        {            mFromIndex = fromIndex;            mEndIndex = endIndex;            mReverse = reverse;            mListener = listener;        }                public boolean getTransformation(long currentTime, Transformation outTransformation) {                        boolean more = super.getTransformation(currentTime, outTransformation);            Log.d("leehong2", "getTransformation    more = " + more);            return more;        }                @Override        protected void applyTransformation(float interpolatedTime, Transformation t)         {            int curIndex = 0;            Interpolator interpolator = this.getInterpolator();            if (null != interpolator)            {                float value = interpolator.getInterpolation(interpolatedTime);                interpolatedTime = value;            }                        if (mReverse)            {                interpolatedTime = 1.0f - interpolatedTime;            }                        curIndex = (int)(mFromIndex + (mEndIndex - mFromIndex) * interpolatedTime);                        if (null != mListener)            {                Log.i("leehong2", "onAnimUpdate  =========== curIndex = " + curIndex);                mListener.onAnimUpdate(curIndex);            }        }    }}


最核心的类

InhaleMesh

package com.nj1s.lib.mesh;import android.graphics.Path;import android.graphics.PathMeasure;public class InhaleMesh extends Mesh{    public enum InhaleDir    {        UP,        DOWN,        LEFT,        RIGHT,    }        private Path mFirstPath  = new Path();    private Path mSecondPath = new Path();    private PathMeasure mFirstPathMeasure  = new PathMeasure();    private PathMeasure mSecondPathMeasure = new PathMeasure();    private InhaleDir mInhaleDir = InhaleDir.DOWN;        public InhaleMesh(int width, int height)    {        super(width, height);    }        public void setInhaleDir(InhaleDir inhaleDir)    {        mInhaleDir = inhaleDir;    }        public InhaleDir getInhaleDir()    {        return mInhaleDir;    }    @Override    public void buildPaths(float endX, float endY)    {        if (mBmpWidth <= 0 || mBmpHeight <= 0)        {            throw new IllegalArgumentException(                    "Bitmap size must be > 0, do you call setBitmapSize(int, int) method?");        }                switch (mInhaleDir)        {        case UP:            buildPathsUp(endX, endY);            break;                    case DOWN:            buildPathsDown(endX, endY);            break;                    case RIGHT:            buildPathsRight(endX, endY);            break;                    case LEFT:            buildPathsLeft(endX, endY);            break;        }    }    @Override    public void buildMeshes(int index)    {        if (mBmpWidth <= 0 || mBmpHeight <= 0)        {            throw new IllegalArgumentException(                    "Bitmap size must be > 0, do you call setBitmapSize(int, int) method?");        }                switch (mInhaleDir)        {        case UP:        case DOWN:            buildMeshByPathOnVertical(index);            break;                    case RIGHT:        case LEFT:            buildMeshByPathOnHorizontal(index);            break;        }    }        public Path[] getPaths()    {        return new Path[] { mFirstPath, mSecondPath };    }        private void buildPathsDown(float endX, float 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);    }        private void buildPathsUp(float endX, float endY)    {        mFirstPathMeasure.setPath(mFirstPath, false);        mSecondPathMeasure.setPath(mSecondPath, false);                float w = mBmpWidth;        float h = mBmpHeight;                mFirstPath.reset();        mSecondPath.reset();        mFirstPath.moveTo(0, h);        mSecondPath.moveTo(w, h);                mFirstPath.lineTo(0, 0);        mSecondPath.lineTo(w, 0);                mFirstPath.quadTo(0, (endY - h) / 2, endX, endY);        mSecondPath.quadTo(w, (endY - h) / 2, endX, endY);    }        private void buildPathsRight(float endX, float 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(0, h);                mFirstPath.lineTo(w, 0);        mSecondPath.lineTo(w, h);                mFirstPath.quadTo((endX + w) / 2, 0, endX, endY);        mSecondPath.quadTo((endX + w) / 2, h, endX, endY);    }        private void buildPathsLeft(float endX, float endY)    {        mFirstPathMeasure.setPath(mFirstPath, false);        mSecondPathMeasure.setPath(mSecondPath, false);                float w = mBmpWidth;        float h = mBmpHeight;                mFirstPath.reset();        mSecondPath.reset();                mFirstPath.moveTo(w, 0);        mSecondPath.moveTo(w, h);                mFirstPath.lineTo(0, 0);        mSecondPath.lineTo(0, h);                mFirstPath.quadTo((endX - w) / 2, 0, endX, endY);        mSecondPath.quadTo((endX - w) / 2, h, endX, endY);    }        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;                if (mInhaleDir == InhaleDir.DOWN)        {            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;                }            }        }        else if (mInhaleDir == InhaleDir.UP)        {            for (int y = HEIGHT; y >= 0; --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 buildMeshByPathOnHorizontal(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 / WIDTH;        float len2 = secondLen / WIDTH;                float firstPointDist  = timeIndex * len1;        float secondPointDist = timeIndex * len2;        float width = mBmpWidth;                mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);        mFirstPathMeasure.getPosTan(firstPointDist + width, 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_X = FIRST_DIST / WIDTH;                mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);        mSecondPathMeasure.getPosTan(secondPointDist + width, 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_X = SECOND_DIST / WIDTH;                if (mInhaleDir == InhaleDir.RIGHT)        {            for (int x = 0; x <= WIDTH; ++x)            {                mFirstPathMeasure.getPosTan(x * FIRST_X + firstPointDist, pos1, null);                mSecondPathMeasure.getPosTan(x * SECOND_X + secondPointDist, pos2, null);                                float h = pos2[1] - pos1[1];                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 y = 0; y <= HEIGHT; ++y)                {                    // x = y * dx / dy                    float fy = y * h / HEIGHT;                    float fx = fy * dx / dy;                                        index = y * (WIDTH + 1) + x;                                        mVerts[index * 2 + 0] = fx + fx1;                    mVerts[index * 2 + 1] = fy + fy1;                }            }        }        else if (mInhaleDir == InhaleDir.LEFT)        {            for (int x = WIDTH; x >= 0; --x)            //for (int x = 0; x <= WIDTH; ++x)            {                mFirstPathMeasure.getPosTan(x * FIRST_X + firstPointDist, pos1, null);                mSecondPathMeasure.getPosTan(x * SECOND_X + secondPointDist, pos2, null);                                float h = pos2[1] - pos1[1];                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 y = 0; y <= HEIGHT; ++y)                {                    // x = y * dx / dy                    float fy = y * h / HEIGHT;                    float fx = fy * dx / dy;                                        index = y * (WIDTH + 1) + WIDTH - x;                                        mVerts[index * 2 + 0] = fx + fx1;                    mVerts[index * 2 + 1] = fy + fy1;                }            }        }    }}


Mesh类的实现


/* * System: CoreLib * @version     1.00 *  * Copyright (C) 2010, LZT Corporation. *  */package com.nj1s.lib.mesh;public abstract class Mesh{    protected int WIDTH      = 40;    protected int HEIGHT     = 40;    protected int mBmpWidth   = -1;    protected int mBmpHeight  = -1;    protected final float[] mVerts;        public Mesh(int width, int height)    {        WIDTH  = width;        HEIGHT = height;        mVerts  = new float[(WIDTH + 1) * (HEIGHT + 1) * 2];    }        public float[] getVertices()    {        return mVerts;    }        public int getWidth()    {        return WIDTH;    }        public int getHeight()    {        return HEIGHT;    }        public static void setXY(float[] array, int index, float x, float y)    {        array[index*2 + 0] = x;        array[index*2 + 1] = y;    }        public void setBitmapSize(int w, int h)    {        mBmpWidth  = w;        mBmpHeight = h;    }        public abstract void buildPaths(float endX, float endY);        public abstract void buildMeshes(int index);    public void buildMeshes(float w, float h)    {        int index = 0;                for (int y = 0; y <= HEIGHT; ++y)        {            float fy = y * h / HEIGHT;            for (int x = 0; x <= WIDTH; ++x)            {                float fx = x * w / WIDTH;                                setXY(mVerts, index, fx, fy);                                index += 1;            }        }    }}




0 0
原创粉丝点击