目錄表
孤獨星球
這是最終的實現效果(左上),主要包括波紋擴散效果、圓球旋轉縮小效果及顏色漸變效果。
此效果是由一個整體的自定義view繪製而成。其中波紋擴散效果,是通過定時改變波紋半徑實現的,此波紋是由先後兩個空心圓組成,在實現過程中要注意時間和各自的尺寸變化。這是參考代碼:
public void startAnima() {
if(drawTimingThread != null) {
drawTimingThread.sendEmptyMessage(MSG_DRAW0);//開始1波紋
float time = (mRMaxRadius - mRMinRadius) / distance * 0.5f;//先取整,再取中
drawTimingThread.sendEmptyMessageDelayed(MSG_DRAW1, (int)(animaBotIntervalTime * time));//定時開啓2波紋
}
}
這是波紋1的半徑變化,參考代碼如下:
if(mCurRadius0 <= mRMaxRadius){
mCurRadius0 += distance;
}else{
mCurRadius0 = mRMinRadius + distance;
}
circlePointF0 = drawCircleOnRipple(MSG_DRAW0, curIndex0);
mRPaint0.setAlpha(getAlphaOfRipple(curIndex0));//透明度
mCirclePaint0.setAlpha(getAlphaOfRipple(curIndex0));
curRadius0 = getRadiusOnRipple(curIndex0);
curIndex0 ++;
if(curIndex0 > (mRMaxRadius - mRMinRadius) / distance)
curIndex0 = 0;
cancleHandle(MSG_DRAW0);
圓球效果同樣是定時繪製的結果,平滑運動只是錯覺。在這裏是每隔200ms(波紋的定時值)在相應的位置進行繪製的,由於波紋擴散週期較短,所以我將圓球的隔旋轉週期定爲了45度,可根據業務自行修改。這裏的難點是在於怎麼找到圓球的圓心座標, 即根據圓心座標,半徑,扇形角度來求扇形終射線與圓弧交叉點的xy座標的問題。這個方法我已經在android自定義view系列之圓環刻度條介紹過了,有興趣的可以看下,或者直接在github上查看項目代碼。圓球參考代碼如下:
private PointF drawCircleOnRipple(int msg, int index) {
//週期開始,隨機初始角度
if(index == 0)
if(msg == MSG_DRAW0)
cirAngel0 = (float) (Math.random() * - 360 + 180);
else
cirAngel1 = (float) (Math.random() * - 360 + 180);
PointF progressPoint = CommentUtils
.calcArcEndPointXY(mRMaxRadius + getPaddingLeft() + mStrokeWidth
, mRMaxRadius + getPaddingTop() + mStrokeWidth
, msg == MSG_DRAW0 ? mCurRadius0 : mCurRadius1
//每個週期旋轉45度
, (msg == MSG_DRAW0 ? curIndex0 : curIndex1) * 1.0f / ((mRMaxRadius - mRMinRadius) / distance) * 45f
, msg == MSG_DRAW0 ? cirAngel0 : cirAngel1);
return progressPoint;
}
圓球的不斷縮小效果,也是定時改變半徑進行繪製的結果,很常規,在這裏就不細說了。波紋和圓球的顏色漸變效果,由於不是漸變到全透明,所以我的alpha取值範圍105-255,參考代碼如下:
private int getAlphaOfRipple(int curIndex) {
int alpha = curIndex * 150 * distance / (mRMaxRadius - mRMinRadius);//只取150的二進制
return 255 - alpha;
}
其餘具體實現方法我就不細說了,源碼我放在了gitHub上,有需要的可以下載或在線看下。
動感環繞
這是最終的實現效果(左下),主要是不斷波動的圓環效果。但跟實際效果相比…嗯有點一言難盡…
這個效果是由四段貝塞爾曲線來擬合實現的。但這種方式出來的效果跟真正的鯨雲音效(動感環繞)差別很大,所以鯨雲音效不太可能是由這種方式實現的。如果有更貼近的實現方法,希望不吝賜教。
這是三階貝塞爾曲線的動態圖及公式,它通過控制曲線上的四個點(起始點、終止點以及兩個相互分離的控制點)來創造、編輯圖形。其中參數 t 的值等於線段上某一個點距離起點的長度除以該線段長度。
這是一階/二階貝塞爾曲線簡單的推導過程,即參數 t 所對應p2座標。三階貝塞爾曲線稍微麻煩點,但思路一樣,也是不斷帶入替換的過程。
由n段三階貝塞爾曲線擬合圓形時,曲線端點到該端點最近的控制點的最佳距離是(4/3)tan(π/(2n))。且t=0.5時的點一定落在圓弧上。
所以當我們想用4條貝塞爾曲線擬合圓,可以進行簡單推導 h 的值:
下面我們就拿四段貝塞爾曲線(h = 0.552284749831)組合成一條完整的圓,作爲我們的初始態。求此 h 這個臨界值的另一個作用是,我們需要運動的b曲線都是向外凸的。起始點和控制點的參考代碼如下:
private void calculateCp() {
b = 0.552284749831;
if(startP == null || endP == null) {
startP = new PointF(0, - mRadius);
endP = new PointF(mRadius, 0);
}
/**
* 平移後的畫布座標,座標(0,0)爲圓心
*/
cp1 = new PointF((float) (mRadius * b), - mRadius);
cp2 = new PointF(mRadius, - (float) (mRadius * b));
}
運動中的圓環,是不斷的隨機更改控制點的座標,併爲起始點添加偏移量的結果,這是一個不斷調試的過程…,需要不斷調整控制點來控制凸起的幅度,很難找到一個完美的效果,難受,上圖效果的座標如下。
private void calculateDynamicCp() {
b = Math.random() * 0.44 + 0.55;
/**
* 平移後的畫布座標,座標(0,0)爲圓心
* 8個控制點和4個起始點,順時針(12點->3點->6點->9點)
*/
if(points != null && points.size() != 0)
points.clear();
points.add(new PointF((float) (Math.random() * - 20 + 10) , - mRadius - (float) (Math.random() * 20)));
points.add(new PointF((float) (mRadius * b), - mRadius - (float) (Math.random() * 20)));
points.add(new PointF(mRadius + (float) (Math.random() * 20), - mRadius - (float) (Math.random() * 10 + 10)));
points.add(new PointF(mRadius + (float) (Math.random() * 10 + 10), (float) (Math.random() * - 20 + 10)));
points.add(new PointF(mRadius + (float) (Math.random() * 20), (float) (Math.random() * 0.5 * mRadius * b + 0.5 *mRadius * b)));
points.add(new PointF((float) (mRadius * b + 10), mRadius + (float) (Math.random() * 20)));
points.add(new PointF((float) (Math.random() * - 20 + 10), mRadius + (float) (Math.random() * 20)));
points.add(new PointF((float) (- mRadius * b), mRadius + (float) (Math.random() * 20)));
points.add(new PointF(- mRadius - (float) (Math.random() * 20), (float) (mRadius * b)));
points.add(new PointF(- mRadius - (float) (Math.random() * 10 + 10), (float) (Math.random() * - 20 + 10)));
points.add(new PointF(- mRadius - (float) (Math.random() * 20), (float) (- mRadius * b)));
points.add(new PointF((float) (- mRadius * b), - mRadius - (float) (Math.random() * 20)));
}
這是繪製方法。初始圓環因爲端點座標是對稱的,所以只需不斷旋轉畫布繪製即可,很簡單。而動態的圓環因爲端點都有了偏移量,所以只能依次繪製四條貝塞爾曲線,每條曲線以lineTo相接。參考代碼如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(mRadius + mStrokeWidth + getPaddingLeft()// 平移畫布
, mRadius + mStrokeWidth + getPaddingTop());
if(isFirst) {
/**
* 旋轉畫布
*/
for(int index = 0; index < 4; index ++) {
canvas.rotate(90f);
bPath.moveTo(startP.x, startP.y);
bPath.cubicTo(cp1.x, cp1.y, cp2.x, cp2.y, endP.x, endP.y);
canvas.drawPath(bPath, bPaint);
bPath.reset();
}
} else {
/**
* 8個控制點和4個起始點,不旋轉
*/
for (int index = 0; index < 4; index ++) {
if(index == 0)
bPath.moveTo(points.get(0).x, points.get(0).y);
else
bPath.lineTo(points.get(index * 3).x, points.get(index * 3).y);
bPath.cubicTo(points.get(index * 3 + 1).x, points.get(index * 3 + 1).y
, points.get(index * 3 + 2).x, points.get(index * 3 + 2).y
, index != 3 ? points.get(index * 3 + 3).x : points.get(0).x
, index != 3 ? points.get(index * 3 + 3).y : points.get(0).y);
}
canvas.drawPath(bPath, bPaint);
bPath.reset();
}
canvas.restore();
}
其餘實現部分我就不細說了,具體的代碼我都放在了gitHub上,有需要的可以下載或在線看下。