Circle Progress 实现原理

原文:http://fichardu.github.io/blog/circle-progress-view/ 

记得看过上面的一个动画设计,就试着实现了一下,首先是可以看到这个动画由两部分组成,一个圆圈的顺时针转动,另一个是圆点的直线运动,圆点之间有时间差,两种运动叠加就形成了这种滚动的效果。

图一、图二、图三

上面图一显示了只有圆圈旋转的状态,图二是只有圆点直线运动的状态,图三是图二中各个圆点添加时间差后的效果。

实现

绘制点

这里一共有15个圆点,相位偏差是360/15=24˚

for (int i = 0; i < POINT_NUM; ++i) {
    float x = mRadius * -(float) Math.sin(DEGREE * 24 * i);
    float y = mRadius * -(float) Math.cos(DEGREE * 24 * i);
    ArcPoint point = new ArcPoint(x, y, COLORS\[i % 3\]);
    mArcPoint\[i\] = point;
}
private final double DEGREE = Math.PI / 180;
private static final int POINT_NUM = 15;

ArcPoint是点的定义,包括位置和颜色

static class ArcPoint {
    float x;
    float y;
    int color;
    ArcPoint(float x, float y, int color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }
}

绘制圆圈的旋转

可以固定点不动,通过旋转canvas来实现

@Override
protected void onDraw(Canvas canvas) {
    canvas.save();
    canvas.translate(mCenter.x, mCenter.y);
    float factor = getFactor();
    canvas.rotate(36 * factor);
    float x, y;
    for (int i = 0; i < POINT_NUM; ++i) {
        mPaint.setColor(mArcPoint\[i\].color);
        // float itemFactor = getItemFactor(i, factor);
        x = mArcPoint\[i\].x;// - 2 * mArcPoint\[i\].x * itemFactor;
        y = mArcPoint\[i\].y;// - 2 * mArcPoint\[i\].y * itemFactor;
        canvas.drawCircle(x, y, mPointRadius, mPaint);
    }
    canvas.restore();
    if (mStartAnim) {
        postInvalidate();
    }
}

这样就可以得到图一圆圈旋转的动画,这里固定了旋转角度36,如果是360就可以看到一个完整的旋转。 这里旋转角度为什么取36?看完整效果,其实每个点在直线移动后,转动1.5个相位偏差也就是24*1.5=36,就可以和其实点的图形重合,而且因为圆点的颜色每三个重复一次,所以经过36度的旋转,新圆点位置和动画开始状态看上去就是一样的。

给圆点添加起始偏移时间

每个点运动的起始时间是不同的,如果一起运动就会得到图二的效果,我们看怎么从总的时间得到每个圆点的运动时间,也就是

float itemFactor = getItemFactor(i, factor);
private float getItemFactor(int index, float factor) {
    float itemFactor = (factor - 0.66f / POINT_NUM * index) * 3;
    if (itemFactor < 0f) {
        itemFactor = 0f;
    } else if (itemFactor > 1f) {
        itemFactor = 1f;
    }
    return mInterpolator.getInterpolation(itemFactor);
}

这里设计每个点直线运动的时间是周期的1/3,那剩余的2/3=0.66就是从第一个点开始到最后一个点开始运动的时间,通过

factor - 0.66f / POINT_NUM * index

就可以得到每个圆点的起始时间,再*3将其换算到直线运动的时间上。 将上面的运动组合起来就得到了完整的onDraw方法

@Override
protected void onDraw(Canvas canvas) {
    canvas.save();
    canvas.translate(mCenter.x, mCenter.y);
    float factor = getFactor();
    canvas.rotate(36 * factor);
    float x, y;
    for (int i = 0; i < POINT_NUM; ++i) {
        mPaint.setColor(mArcPoint\[i\].color);
        float itemFactor = getItemFactor(i, factor);
        x = mArcPoint\[i\].x - 2 * mArcPoint\[i\].x * itemFactor;
        y = mArcPoint\[i\].y - 2 * mArcPoint\[i\].y * itemFactor;
        canvas.drawCircle(x, y, mPointRadius, mPaint);
    }
    canvas.restore();
    if (mStartAnim) {
        postInvalidate();
    }
}

Source code

源码在这里 https://github.com/Fichardu/CircleProgress