本文主要是介绍Android 吸入动画效果详解(仿mac退出效果),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
[转]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 / dxfloat 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;@Overrideprotected 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;@Overridepublic 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);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.inhale_anim_menu, menu);return true;}@Overridepublic 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();}@Overrideprotected 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(){@Overridepublic 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 pathsmPaint.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;}@Overrideprotected 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;}@Overridepublic 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;}}@Overridepublic 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 / dxfloat 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 / dxfloat 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 / dyfloat 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 / dyfloat 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;}}}
}
这篇关于Android 吸入动画效果详解(仿mac退出效果)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!