How to animate a path on canvas - android How to animate a path on canvas - android android android

How to animate a path on canvas - android


You can transform your canvas by time, i.e:

class MyView extends View {    int framesPerSecond = 60;    long animationDuration = 10000; // 10 seconds    Matrix matrix = new Matrix(); // transformation matrix    Path path = new Path();       // your path    Paint paint = new Paint();    // your paint    long startTime;    public MyView(Context context) {        super(context);        // start the animation:        this.startTime = System.currentTimeMillis();        this.postInvalidate();     }    @Override    protected void onDraw(Canvas canvas) {        long elapsedTime = System.currentTimeMillis() - startTime;        matrix.postRotate(30 * elapsedTime/1000);        // rotate 30° every second        matrix.postTranslate(100 * elapsedTime/1000, 0); // move 100 pixels to the right        // other transformations...        canvas.concat(matrix);        // call this before drawing on the canvas!!        canvas.drawPath(path, paint); // draw on canvas        if(elapsedTime < animationDuration)            this.postInvalidateDelayed( 1000 / framesPerSecond);    }}


try this:

class PathDrawable extends Drawable implements AnimatorUpdateListener  {    private Path mPath;    private Paint mPaint;    private ValueAnimator mAnimator;    public PathDrawable() {        mPath = new Path();        mPaint = new Paint();        mPaint.setColor(0xffffffff);        mPaint.setStrokeWidth(5);        mPaint.setStyle(Style.STROKE);    }    public void startAnimating() {        Rect b = getBounds();        mAnimator = ValueAnimator.ofInt(-b.bottom, b.bottom);        mAnimator.setDuration(1000);        mAnimator.addUpdateListener(this);        mAnimator.start();    }    @Override    public void draw(Canvas canvas) {        canvas.drawPath(mPath, mPaint);    }    @Override    public void setAlpha(int alpha) {    }    @Override    public void setColorFilter(ColorFilter cf) {    }    @Override    public int getOpacity() {        return PixelFormat.TRANSLUCENT;    }    @Override    public void onAnimationUpdate(ValueAnimator animator) {        mPath.reset();        Rect b = getBounds();        mPath.moveTo(b.left, b.bottom);        mPath.quadTo((b.right-b.left)/2, (Integer) animator.getAnimatedValue(), b.right, b.bottom);        invalidateSelf();    }}

to test it add in your onCreate method:

TextView view = new TextView(this);view.setText("click me");view.setTextColor(0xffcccccc);view.setGravity(Gravity.CENTER);view.setTextSize(48);final PathDrawable d = new PathDrawable();view.setBackgroundDrawable(d);OnClickListener l = new OnClickListener() {    @Override    public void onClick(View v) {        d.startAnimating();    }};view.setOnClickListener(l);setContentView(view);


You can create a PathMeasure for your path and determine the length of it:

private PathMeasure pathMeasure; // field// After you've created your pathpathMeasure = new PathMeasure(path, false); pathLength = pathMeasure.getLength();

You can then get get a specific point on the path using getPosTan() inside, say, a ValueAnimator's update listener:

final float[] position = new float[2]; // field// Inside your animation's update method, where dt is your 0..1 animated fractionfinal float distance = dt * pathLength;pathMeasure.getPosTan(distance, position, null);// If using onDraw you'll need to tell the view to redraw using the new positioninvalidate(); 

You can then make use of the position in onDraw (or whatever).

canvas.drawCircle(position[0], position[1], radius, paint);

The advantages of this approach is that it is straightforward, doesn't require chunky maths, and works on all APIs.

If using API 21+, you can use a ValueAnimator and pass in a Path to use its positions, which is simpler. Example SO question.