今天继续分(Zhuang)享(Bi)一个自定义的动画历程。今天的目标以下: java
简单看出,整个动画分红三个部分:车轮,车箱,车顶。git
车轮:车轮的动效比较简单,基于X轴的横向拉伸和挤压,这个不难。github
车箱,车顶:这两个部分其实动效是同样的,区别是,动做的激烈程度不同 (/ω\)。要作到这种姿式这种动做,噢,不,是动效,有点难。 但仔细观察,车箱比如是有人用手指,在车箱的左下和右下进行拉扯和复原所达到的效果, Canvas提供了一个方法:canvas
drawBitmapMesh(Bitmap bitmap,
int meshWidth,
int meshHeight,
float[] verts,
int vertOffset,
int[] colors,int colorffset,Paint paint)
复制代码
这个方法能够对bitmap进行扭曲,部分参数说明以下:数组
//将图像分红多少格
private int carBodyWidthPart = 4;
private int carBodyHeightPart = 4;
//交点坐标的个数
private int carBodyPartCount = (carBodyWidthPart + 1) * (carBodyHeightPart + 1);
//用于保存COUNT的坐标
//x0, y0, x1, y1......
private float[] carBodyNewsPos = new float[carBodyPartCount * 2];
private float[] carBodyOrigPos = new float[carBodyPartCount * 2];
/** * 获得网格点集合 * @param bitmap bitmap * @param heightPart 纵向分割的份数 * @param widthPart 横向分割的份数 * @param newPos 新点集合 * @param origPos 原始点集合 */
protected void getBitmapMeshPoints(Bitmap bitmap, int heightPart, int widthPart, float[] newPos, float[] origPos) {
//初始化网格点
int index = 0;
float bmWidth = bitmap.getWidth();
float bmHeight = bitmap.getHeight();
for (int i = 0; i < heightPart + 1; i++) {
float fy = bmHeight * i / heightPart;
for (int j = 0; j < widthPart + 1; j++) {
float fx = bmWidth * j / widthPart;
//X轴坐标 放在偶数位
newPos[index * 2] = fx;
origPos[index * 2] = newPos[index * 2];
//Y轴坐标 放在奇数位
newPos[index * 2 + 1] = fy;
origPos[index * 2 + 1] = newPos[index * 2 + 1];
index += 1;
}
}
}
复制代码
/** * 绘制网格点 * @param canvas canvas * @param pos 点集 * @param paint 画笔 */
protected void drawPoint(Canvas canvas, float[] pos, Paint paint) {
for (int i = 0; i < pos.length/2; i++) {
int x = i*2;
int y = x + 1;
canvas.drawPoint(pos[x], pos[y], paint);
}
}
复制代码
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.setDrawFilter(paintFlagsDrawFilter);
//原始点
canvas.save();
canvas.translate(50,50);
canvas.drawBitmapMesh(bitmapCarBody, carBodyWidthPart, carBodyHeightPart, carBodyOrigPos, 0, null, 0, paint);
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
drawPoint(canvas, carBodyOrigPos, paint);
canvas.restore();
//变化点
canvas.save();
canvas.translate(1000,50);
canvas.drawBitmapMesh(bitmapCarBody, carBodyWidthPart, carBodyHeightPart, carBodyNewsPos, 0, null, 0, paint);
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
drawPoint(canvas, carBodyNewsPos, paint);
canvas.restore();
}
复制代码
既然要形变要扭曲,那么咱们先对carBodyNewsPos这个点集进行肆意妄为地操做一下:app
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.setDrawFilter(paintFlagsDrawFilter);
//原始点
canvas.save();
canvas.translate(50,50);
canvas.drawBitmapMesh(bitmapCarBody, carBodyWidthPart, carBodyHeightPart, carBodyOrigPos, 0, null, 0, paint);
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
drawPoint(canvas, carBodyOrigPos, paint);
canvas.restore();
//遍历交点,修改
for (int i = 0; i < carBodyPartCount; i++) {
int x = i * 2;
int y = x + 1;
carBodyNewsPos[x] = carBodyOrigPos[x] * (changeFactor * 1.0f / 100 + 1);
}
//变化点
canvas.save();
canvas.translate(1000,50);
canvas.drawBitmapMesh(bitmapCarBody, carBodyWidthPart, carBodyHeightPart, carBodyNewsPos, 0, null, 0, paint);
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
drawPoint(canvas, carBodyNewsPos, paint);
canvas.restore();
}
复制代码
咱们对每个点应用了:carBodyNewsPos[x] = carBodyOrigPos[x] * (changeFactor * 1.0f / 100 + 1)
:ide
仔细观察车箱的动画,发现有几点细节:函数
由于咱们是依次遍历点集,就是从i=0一直到i=posCount,那么咱们来实现一下上面的变化:性能
大体获得X方向的扭曲度:changeFactor*Math.abs((widthPart+1)/2-i%(widthPart+1))*(origPos[y]/bitmap.getHeight()) * xFactor;
动画
bitmap.getHeight()
就是y轴的最大值,那么,origPos[y]/bitmap.getHeight()
就是当前点所占y轴的百分比,也就是,离y=0越近,origPos[y]/bitmap.getHeight()
的值越小。Math.abs((widthPart+1)/2-i%(widthPart+1))
(widthPart+1)/2
是算出中心轴的坐标相对位置,例如图中,算出应该是2,那么中心轴的相对位置则为2,而i%(widthPart+1)
,在图中就是,0-4的取值,跟中心轴的相对位置2相减取绝对值,就是一个离中心轴的变化量的大小了。Y方向的扭曲度,也是同样的。这里就不说了。。。最后得出的扭曲函数以下:
/** * 改变车箱的Points * @param bitmap bitmap * @param posCount 点的个数 * @param widthPart 横向分割的份数 * @param heightPart 纵向分割的份数 * @param newPos 新点集合 * @param origPos 原始点集合 * @param xFactor 变化因子 * @param yFactor 变化因子 */
protected void changeCarBodyPoint(Bitmap bitmap, int posCount, int widthPart, int heightPart, float[] newPos, float[] origPos, float xFactor, float yFactor) {
//遍历交点,修改
for (int i = 0; i < posCount; i++) {
int x = i*2;
int y = x + 1;
//x方向的拉伸,离x周轴线越近变化越小,离顶部(y=0)越近,变化也越小
if (newPos[x]<centralAxisX) {
newPos[x] = origPos[x] - changeFactor*Math.abs((widthPart+1)/2-i%(widthPart+1))*(origPos[y]/bitmap.getHeight()) * xFactor;
}
else if (newPos[x]>centralAxisX) {
newPos[x] = origPos[x] + changeFactor*Math.abs((widthPart+1)/2-i%(widthPart+1))*(origPos[y]/bitmap.getHeight()) * xFactor;
}
//y方向的拉伸,离x周轴线越近变化越大,离顶部(y=0)越近,变化也越小
newPos[y] = origPos[y] + changeFactor * (widthPart*heightPart/(heightPart+1)+1) * (1 - Math.abs(origPos[x]-centralAxisX)/bitmap.getWidth()) * yFactor;
}
}
复制代码
把上面的函数应用上去:
是吧,效果差很少就得了吧!(呕心沥血,个人40米长刀呢!)
drawBitmapMesh
的使用xFactor
和yFactor