Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android draw ball trail

There are balls in my app that just fly through display. They draws as I want. But now I want to draw the trail behind them.

All I could make is just drawing by canvas.drawPath something like following picture:

I have it now

But it is not what I want. It should have pointed tail and gradient color like this:

I want that

I have no idea how to make it. Tried BitmapShader - couldn't make something right. Help, please.

Code:

First of all, there is Point class for position on display:

class Point {
    float x, y;
    ...
}

And trail is stored as queue of Point:

private ConcurrentLinkedQueue<Point> trail;

It doesn't matter how it fills, just know it has size limit:

trail.add(position);
if(trail.size() > TRAIL_MAX_COUNT) {
    trail.remove();
}

And drawing happened in DrawTrail method:

private void DrawTrail(Canvas canvas) {
    trailPath.reset();
    boolean isFirst = true;
    for(Point p : trail) {
        if(isFirst) {
            trailPath.moveTo(p.x, p.y);
            isFirst = false;
        } else {
            trailPath.lineTo(p.x, p.y);
        }
    }
    canvas.drawPath(trailPath, trailPaint);
}

By the way, trailPaint is just really fat paint :)

trailPaint = new Paint();
trailPaint.setStyle(Paint.Style.STROKE);
trailPaint.setColor(color);
trailPaint.setStrokeWidth(radius * 2);
trailPaint.setAlpha(150);
like image 392
Ircover Avatar asked Feb 21 '17 16:02

Ircover


2 Answers

I see you want to see a gradient on the ball path, you could use something like this

 int x1 = 0, y1 = 0, x2 = 0,  y2 = 40;
 Shader shader = new LinearGradient(0, 0, 0, 40, Color.WHITE, Color.BLACK, TileMode.CLAMP);
trailPaint = new Paint(); 
trailPaint.setShader(shader); 

This is what you should change your trailPaint to and see if it works.

provided from here.

like image 118
Destry Avatar answered Oct 22 '22 05:10

Destry


I found solution. But still think it is not the best one.

First of all there are my class fields used for that task.

static final int TRAIL_MAX_COUNT = 50; //maximum trail array size
static final int TRAIL_DRAW_POINT = 30; //number of points to split the trail for draw

private ConcurrentLinkedQueue<Point> trail;
private Paint[] trailPaints;
private float[][] trailPoss, trailTans;
private Path trailPath;

Additionally to trailPath object I used PathMeasure object to split path to multiple equal parts.

After filling trail array object added call of trail calculating function.

lastTrailAdd = now;
trail.add(pos.Copy());
if (trail.size() > TRAIL_MAX_COUNT) {
    trail.remove();
}
FillTrail();

Then my FillTrail function.

private void FillTrail() {
    trailPath.reset();
    boolean isFirst = true;
    for(Point p : trail) {
        if(isFirst) {
            trailPath.moveTo(p.x, p.y);
            trailPoss[0][0] = p.x;
            trailPoss[0][1] = p.y;
            isFirst = false;
        } else {
            trailPath.lineTo(p.x, p.y);
        }
    }
    PathMeasure path = new PathMeasure(trailPath, false);
    float step = path.getLength() / TRAIL_DRAW_POINT;
    for(int i=0; i<TRAIL_DRAW_POINT; i++) {
        path.getPosTan(step * i, trailPoss[i], trailTans[i]);
    }
}

It separated from drawing thread. Next code is drawing function.

private void DrawTrail(Canvas canvas) {
    if(trail.size() > 1) {
        float prevWidthHalfX = 0f, prevWidthHalfY = 0f, prevX = 0f, prevY = 0f;
        Path trailStepRect = new Path();
        boolean isFirst = true;
        for (int i = 0; i < TRAIL_DRAW_POINT; i++) {
            float currWidthHalf = (float) (radius) * i / TRAIL_DRAW_POINT / 2f,
                    currWidthHalfX = currWidthHalf * trailTans[i][1],
                    currWidthHalfY = currWidthHalf * trailTans[i][0],
                    currX = trailPoss[i][0], currY = trailPoss[i][1];
            if (!isFirst) {
                trailStepRect.reset();
                trailStepRect.moveTo(prevX - prevWidthHalfX, prevY + prevWidthHalfY);
                trailStepRect.lineTo(prevX + prevWidthHalfX, prevY - prevWidthHalfY);
                trailStepRect.lineTo(currX + currWidthHalfX, currY - currWidthHalfY);
                trailStepRect.lineTo(currX - currWidthHalfX, currY + currWidthHalfY);
                canvas.drawPath(trailStepRect, trailPaints[i]);
            } else {
                isFirst = false;
            }
            prevX = currX;
            prevY = currY;
            prevWidthHalfX = currWidthHalfX;
            prevWidthHalfY = currWidthHalfY;
        }
    }
}

Main point of this is drawing trail by parts with different paints. Closer to ball - wider the trail. I think I will optimise it, but it is allready work.

If you want to watch how it looks just install my app from google play.

like image 30
Ircover Avatar answered Oct 22 '22 06:10

Ircover