記得看過上面的一個動畫設計,就試着實作了一下,首先是可以看到這個動畫由兩部分組成,一個圓圈的順時針轉動,另一個是圓點的直線運動,圓點之間有時間差,兩種運動疊加就形成了這種滾動的效果。
圖一、圖二、圖三
上面圖一顯示了隻有圓圈旋轉的狀态,圖二是隻有圓點直線運動的狀态,圖三是圖二中各個圓點添加時間差後的效果。
實作
繪制點
這裡一共有15個圓點,相位偏差是360/15=24˚
for (int i = ; i < POINT_NUM; ++i) {
float x = mRadius * -(float) Math.sin(DEGREE * * i);
float y = mRadius * -(float) Math.cos(DEGREE * * i);
ArcPoint point = new ArcPoint(x, y, COLORS[i % ]);
mArcPoint[i] = point;
}
private final double DEGREE = Math.PI / ;
private static final int POINT_NUM = ;
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( * factor);
float x, y;
for (int i = ; 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 - f / POINT_NUM * index) * ;
if (itemFactor < f) {
itemFactor = f;
} else if (itemFactor > f) {
itemFactor = f;
}
return mInterpolator.getInterpolation(itemFactor);
}
這裡設計每個點直線運動的時間是周期的
1/3
,那剩餘的
2/3=0.66
就是從第一個點開始到最後一個點開始運動的時間,通過
factor - f / POINT_NUM * index
就可以得到每個圓點的起始時間,再
*3
将其換算到直線運動的時間上。
将上面的運動組合起來就得到了完整的
onDraw
方法
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(mCenter.x, mCenter.y);
float factor = getFactor();
canvas.rotate( * factor);
float x, y;
for (int i = ; i < POINT_NUM; ++i) {
mPaint.setColor(mArcPoint[i].color);
float itemFactor = getItemFactor(i, factor);
x = mArcPoint[i].x - * mArcPoint[i].x * itemFactor;
y = mArcPoint[i].y - * mArcPoint[i].y * itemFactor;
canvas.drawCircle(x, y, mPointRadius, mPaint);
}
canvas.restore();
if (mStartAnim) {
postInvalidate();
}
}
Source code
源碼在這裡 https://github.com/Fichardu/CircleProgress