Related recommendations:

  • The Android source code to read | Handler
  • Android custom View | wave motion effect
  • Programming knowledge | introduction to coding format

1. An overview of the

Zhuang enjoys a custom animation journey today. Here are my goals for today:

The UI source file for this animation is missing, i.e. the details of the animation are not known (ahem), so go for it.

2. Simple analysis

It can be seen that the whole animation is divided into three parts: wheel, carriage and roof.

  • Wheel: The dynamic effect of the wheel is relatively simple, based on the horizontal stretching and extrusion of the X axis, this is not difficult.

  • Carriage, roof: these two parts are actually the same dynamic effect, the difference is that the intensity of the action is not the same

(/ omega \). It’s kind of hard to do this kind of posture, this kind of movement, oh, no, motor effect. However, after careful observation, the carriage is just like the effect achieved by someone pulling and restoring the left and right sides of the carriage with fingers. Canvas provides a method:

drawBitmapMesh(Bitmap bitmap, 
    int meshWidth, 
    int meshHeight, 
    float[] verts, 
    int vertOffset, 
    int[] colors,int colorffset,Paint paint)
Copy the code

This method can be used to distort a bitmap. Some of the parameters are described as follows:

  • Bitmaps require distorted source bitmaps
  • MeshWidth controls how many grids the source bitmap is divided into horizontally
  • MeshHeight controls how many grids the source bitmap is divided vertically
  • Verts are an array of length (meshWidth + 1) * (meshHeight + 1) * 2, recording the positions of the vertices of the distorted bitmap
  • VertOffset controls the verts array, where the bitmap is distorted from the first element of the array

3. drawBitmapMeshA simple example

  • We initialize these parameters according to their meaning:
// How many cells to divide the image into
private int carBodyWidthPart = 4;
private int carBodyHeightPart = 4;
// The number of coordinates of intersections
private int carBodyPartCount = (carBodyWidthPart + 1) * (carBodyHeightPart + 1);
// Hold the coordinates of COUNT
//x0, y0, x1, y1......
private float[] carBodyNewsPos = new float[carBodyPartCount * 2];
private float[] carBodyOrigPos = new float[carBodyPartCount * 2];
/** * get the set of grid points *@param bitmap bitmap
 * @paramHeightPart Number of vertically split shares *@paramWidthPart Number of horizontally divided shares *@paramNewPos Collection of new points *@paramOrigPos set of original points */
protected void getBitmapMeshPoints(Bitmap bitmap, int heightPart, int widthPart, float[] newPos, float[] origPos) {
    // Initialize grid points
    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;
            // Put the X coordinate in even digits
            newPos[index * 2] = fx;
            origPos[index * 2] = newPos[index * 2];
            // Put the Y coordinates in odd digits
            newPos[index * 2 + 1] = fy;
            origPos[index * 2 + 1] = newPos[index * 2 + 1];
            index += 1; }}}Copy the code
  • So I’m going to initialize the array as a function, and notice that the array is stored in the order x1,y1 at the first point, and x2,y2 at the second point. So let’s just draw the graph and see what it looks like, and let’s draw the set of points:
/** * Draw grid points *@param canvas canvas
 * @paramPos * point set@paramPaint brush * /
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); }}Copy the code
  • Drawing method:
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.setDrawFilter(paintFlagsDrawFilter);
    / / the original point
    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();
    / / change point
    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();
}
Copy the code

  • The one on the left is the original point, and the one on the right is the changed point, because I didn’t shift the point so it’s the same thing. It is also important to note that drawing the drawBitmapMesh does not pass in the starting point of the drawing, because it is drawn from the original point by default, so there is code to move the coordinates.

4. Try a little twist

Since we want to deform and distort, let’s first arbitrarily manipulate the point set carBodyNewsPos:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.setDrawFilter(paintFlagsDrawFilter);
    / / the original point
    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();
    // Iterate through the intersection and modify it
    for (int i = 0; i < carBodyPartCount; i++) {
        int x = i * 2;
        int y = x + 1;
        carBodyNewsPos[x] = carBodyOrigPos[x] * (changeFactor * 1.0 f / 100 + 1);
    }
    / / change point
    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();
}
Copy the code

For each point we applied: carBodyNewsPos[x] = carBodyOrigPos[x] * (changeFactor * 1.0f / 100 + 1) :

5. Carriage distortion realization

A closer look at the animation of the carriage reveals several details:

  • The twists can be broken down into x – and Y-axis twists, which add up to an animation of the carriage
  • The car is the same as the roof distortion, not the distortion
  • The X-axis distortion, with the central axis of the carriage as the dividing line, left to left distortion, right to right distortion
  • The tension in the X-axis direction has a smaller change as it gets closer to the central X-axis, and a smaller change as it gets closer to the top (y=0)
  • The distortion of the Y-axis, with y=0 as the central axis, changes more the closer it is to the central axis of x, and changes less the closer it is to the top (y=0)

Since we are traversing the set of points from I =0 to I =posCount, let’s implement the change above:

  • Set two factors: xFactor and yFactor to control the maximum distortion of x and Y axes.
  • Set the changeFactor to drive the distortion to change over time
  • When x of a point is to the left of the central x axis, its x coordinate should be the original coordinate minus the degree of distortion, and vice versa

ChangeFactor * math.abs ((widthPart+1)/2-i%(widthPart+1))*(origPos[y]/ bitmap.getheight ()) * xFactor;

  • The xFactor controls the overall degree of distortion
  • The closer you get to the top (y=0), the less it changes, and since I’m drawing from y=0,bitmap.getHeight()That’s the maximum value of the Y-axis, so,origPos[y]/bitmap.getHeight()It’s the percentage of the y axis, so the closer I get to y equals 0,origPos[y]/bitmap.getHeight()The smaller the value of.
  • The closer you get to the x axis, the smaller the change,Math.abs((widthPart+1)/2-i%(widthPart+1))

As shown in the figure above, red is the central axis of x, and the value of I (the number of points) we traverse is from 0 to N, while I =0, I =4 is the largest, I =1, I =3 times smaller, and so on.(widthPart+1)/2I’m going to figure out the relative position of the central axis, so if I figure out the relative position of the central axis is 2, then the relative position of the central axis is 2, andi%(widthPart+1)In this case, it’s 0 minus 4, minus the absolute value of 2 relative to the central axis, which is the magnitude of a change in value off the central axis.

In the Y direction, it’s the same thing. I won’t say it here… The final distortion function is as follows:

/** * Change the carriage Points *@param bitmap bitmap
 * @paramPosCount Number of points *@paramWidthPart Number of horizontally divided shares *@paramHeightPart Number of vertically split shares *@paramNewPos Collection of new points *@paramOrigPos collection of primitive points *@paramXFactor Change factor *@paramYFactor Change factor */
protected void changeCarBodyPoint(Bitmap bitmap, int posCount, int widthPart, int heightPart, float[] newPos, float[] origPos, float xFactor, float yFactor) {
    // Iterate through the intersection and modify it
    for (int i = 0; i < posCount; i++) {
        int x = i*2;
        int y = x + 1;
        // In the x direction, the closer it is to the x axis, the smaller the change is; in the x direction, the closer it is to the top (y=0), the smaller the change is
        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;
        }
        // In the y direction, the closer it is to the X-axis, the greater the change is; and the closer it is to the top (y=0), the smaller the change is
        newPos[y] = origPos[y] + changeFactor * (widthPart*heightPart/(heightPart+1) +1) * (1- Math.abs(origPos[x]-centralAxisX)/bitmap.getWidth()) * yFactor; }}Copy the code

Apply the above function:

Oh, right? It’s almost as good as it gets! (Sweat and sweat, where’s my 40-meter knife!)

6. Summary

  • Through this custom animation, I’m familiar with itdrawBitmapMeshThe use of
  • The roof of the car is also the same twisted animation, but to tunexFactorandyFactor
  • The Demo here
  • It is said that Matrix can be used to do it? We’ll look into that later
  • Attention should be paid to the performance consumption caused by point density and the poor effect caused by point sparsity, which should be selected according to the specific situation

Code word is not easy, convenient if the quality of three even, or pay attention to my public number technology sauce, focus on Android technology, not regularly push fresh articles, if you have a good article to share with you, welcome to pay attention to contribute!