如何在Android画布上制作路径动画。

44

是否可以将动画师附加到路径上?

在Canvas上绘制动态线条还有其他方法吗?

我在发布问题之前进行了搜索,但是没有找到任何东西。在两个其他帖子(在Android上)以动画形式绘制路径如何在Android Canvas上绘制路径并实现动画效果中有一些解决方法,但对我不起作用。

我在onDraw方法中发布了我的代码,以说明我想要的内容。

paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(2);
    paint.setColor(Color.BLACK);

    Path path = new Path();
    path.moveTo(10, 50);   // THIS TRANSFORMATIONS TO BE ANIMATED!!!!!!!!
    path.lineTo(40, 50);
    path.moveTo(40, 50);
    path.lineTo(50, 40);
    // and so on...

    canvas.drawPath(path, paint);
3个回答

55

您可以按时间轴转换您的画布,即:

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);
    }

}

圆形怎么样?我的意思是,我如何动画绘制弧线或椭圆形? - user3111850
做了两个更改。1:添加invalidate(); 2. matrix.postRotate(30.0f); 行matrix.postRotate(30 * elapsedTime/1000);会使您的动画过快。 - Faizan Mubasher
5
如果你想要精确控制动画的时间,帧速率的方法并不是一个好的选择,但它非常适合小型特效。 - slott
@slott 说得好,但你如何做出更精确的动画呢? - ggorlen
1
对于更复杂的动画,我会使用纹理视图来确保最佳绘制性能。然后您可以运行一个线程来精确计时并指导主渲染线程。 - slott

26

试一下这个:

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();
    }
}

在您的 onCreate 方法中添加以下代码以进行测试:

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);

2
立即绘制直线,无需动画。 - azizbekian
代码可以工作,但我想设置任何颜色的圆形背景,以及像您所提到的描边。有什么想法吗? - Sagar Pujari
这应该是被接受的答案...可以进行微调以影响所有绘图..比如; mAnimator = ValueAnimator.ofInt(0.0f, 1.0f); 然后你可以将所有值乘以动画值。 - Jessicardo
哦,我实际上是在回复@Jessicardo时忘记标记用户了。他说这应该是答案,所以我只是问他是否这样说仅仅因为这对他有效,还是他的评论基于某些基准(或文档)。 - Farid
@FARID 因为这个答案是动态的,可以扩展到各种动画。这也是正确的做法。 - Jessicardo
显示剩余2条评论

4
您可以为路径创建一个PathMeasure,并确定其长度:
private PathMeasure pathMeasure; // field

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

您可以在内部使用getPosTan()获取路径上的特定点,例如在ValueAnimator的更新监听器中:
final float[] position = new float[2]; // field

// Inside your animation's update method, where dt is your 0..1 animated fraction
final float distance = dt * pathLength;
pathMeasure.getPosTan(distance, position, null);

// If using onDraw you'll need to tell the view to redraw using the new position
invalidate(); 

你可以在onDraw(或任何其他方法)中使用该位置。
canvas.drawCircle(position[0], position[1], radius, paint);

这种方法的优点是简单明了,不需要复杂的数学运算,并适用于所有API。
如果使用API 21+,您可以使用ValueAnimator并传入一个路径来使用其位置,这更为简单。示例SO问题

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接