在逛UI中国时,看到一个阳光加载小动画,觉得好喜欢,于是就也想实现一下,并在我的App中使用:
于是就在想有什么方法可以画出螺旋线,起初我想通过螺旋线的参数方程进行实现,无奈在网上搜来搜去也只找到它的极坐标方程,且不容易转换成直角坐标方程,后来我又想到一个方法,就是通过drawArc()方法画若干个90°的圆弧进行实现,具体思路如图
后来发现这样子画出的螺旋线特别别扭,而且不知道在没去实现这样一个动画,就又想啦另外一种实现的思路,通过ObjectAnimator来进行实现,具体思路如下:
即通过ObjectAnimator同时扩大圆弧的起始角度和半径,这样子又可以实现动画,切画出的螺旋线也比较圆滑,之后即通过一定的三角函数来控制那5点阳光的位置,并把它画出来,大致的实现效果为(gif图制作有点粗糙)
具体代码如下:
自定View
SunnyLoad.java
public class SunnyLoad extends View {
//View's width
private int VIEW_WIDTH;
//View's height
private int VIEW_HEIGHT;
//画笔
private Paint mPaint;
//圆弧的半径
private float mRadius;
//圆弧的起始角度
private float mStartAngel = 0f;
//缓存位图
private Bitmap mCacheBitmap;
//缓存画布
private Canvas mCacheCanvas;
//是否为第一个动画
private boolean isFirstAnim = true;
//最大半径
private float maxRadius = -1;
//最小半径
private float minRadius = -1;
//光线长度
private float mLineLength;
//光线最大长度
private float maxLineLength = -1;
//一次完整动画是否结束的标志
private boolean isFinished = false;
private RectF mRectF;
private int index = 0;
private static final int INDEX_ONE = 0;
private static final int INDEX_TWO = 1;
private static final int INDEX_THREE = 2;
private static final int INDEX_FOUR = 3;
private static final int INDEX_FIVE = 4;
private static final int[] INDEXES = new int[]{INDEX_ONE, INDEX_TWO, INDEX_THREE, INDEX_FOUR, INDEX_FIVE};
public SunnyLoad(Context context) {
super(context);
init();
}
public SunnyLoad(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SunnyLoad(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mRectF = new RectF();
mPaint = new Paint();
mPaint.setColor(Color.YELLOW);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(30f);
postDelayed(new Runnable() {
@Override
public void run() {
startFirstAnim(0);
}
},1000);
invalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
VIEW_WIDTH = getWidth();
VIEW_HEIGHT = getHeight();
mCacheBitmap = Bitmap.createBitmap(VIEW_WIDTH, VIEW_HEIGHT, Bitmap.Config.ARGB_8888);
mCacheCanvas = new Canvas(mCacheBitmap);
}
/**
* 设置画笔的颜色和粗细
*
* @param color 画笔颜色
* @param width 画笔粗细
*/
public void setColorAndPaintWidth(int color, float width) {
mPaint.setColor(color);
mPaint.setStrokeWidth(width);
invalidate();
}
private void startFirstAnim(long delayed) {
if (maxRadius == -1) maxRadius = this.VIEW_WIDTH * 0.1492f;
if (minRadius == -1) minRadius = this.VIEW_WIDTH * 0.0333f;
if (maxLineLength == -1) maxLineLength = this.maxRadius * 0.3333f;
// System.out.println("FirstAnim");
AnimatorSet animatorSet = new AnimatorSet();
ObjectAnimator angel_animator = ObjectAnimator.ofFloat(this, "startAngel", -60, 450);
ObjectAnimator radius_animator = ObjectAnimator.ofFloat(this, "radius", minRadius, maxRadius);
animatorSet.playTogether(angel_animator, radius_animator);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
isFirstAnim=false;
index = INDEX_ONE;
startSecondAnim();
super.onAnimationEnd(animation);
}
});
animatorSet.setDuration(2500);
animatorSet.setStartDelay(delayed);
animatorSet.start();
}
private void startSecondAnim() {
// System.out.println("SecondAnim");
ObjectAnimator length_animator = ObjectAnimator.ofFloat(this, "lineLength", 0, maxLineLength);
length_animator.setDuration(200);
length_animator.setInterpolator(new AccelerateInterpolator());
length_animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (index != INDEXES.length) {
index++;
startSecondAnim();
} else {
isFirstAnim=true;
isFinished =true;
}
}
});
length_animator.start();
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (isFinished) {
//清除缓存
mCacheCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
startFirstAnim(1000);
isFinished=false;
return;
}
if (isFirstAnim) {
mRectF.left = VIEW_WIDTH * 0.4854f - mRadius;
mRectF.right = VIEW_WIDTH * 0.4854f + mRadius;
mRectF.top = VIEW_HEIGHT * 0.5f - mRadius;
mRectF.bottom = VIEW_HEIGHT * 0.5f + mRadius;
mCacheCanvas.drawArc(mRectF, mStartAngel, 20, false, mPaint);
canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
}
if (!isFirstAnim) {
switch (index) {
case INDEX_ONE:
mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.9397f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.3420f, VIEW_WIDTH * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.9397f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.3420f, mPaint);
canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
break;
case INDEX_TWO:
mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.5000f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.8660f, VIEW_WIDTH * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.5000f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.8660f, mPaint);
canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
break;
case INDEX_THREE:
mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f + (maxRadius + VIEW_WIDTH * 0.04255f) * 0.1736f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.9848f, VIEW_WIDTH * 0.5f + ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.1736f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.9848f, mPaint);
canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
break;
case INDEX_FOUR:
mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f + (maxRadius + VIEW_WIDTH * 0.04255f) * 0.7660f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.6428f, VIEW_WIDTH * 0.5f + ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.7660f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.6428f, mPaint);
canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
break;
case INDEX_FIVE:
mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f + (maxRadius + VIEW_WIDTH * 0.04255f), VIEW_HEIGHT * 0.5f, VIEW_WIDTH * 0.5f + ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength), VIEW_HEIGHT * 0.5f, mPaint);
canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint);
break;
}
}
}
public void setMaxRaius(float maxRadius) {
this.maxRadius = maxRadius;
}
public float getMaxRaius() {
return maxRadius;
}
public float getStartAngel() {
return mStartAngel;
}
public void setStartAngel(float startAngel) {
mStartAngel = startAngel;
invalidate();
}
public float getLineLength() {
return mLineLength;
}
public float getMaxLineLength() {
return maxLineLength;
}
public void setMaxLineLength(float maxLineLength) {
this.maxLineLength = maxLineLength;
}
public void setLineLength(float lineLength) {
mLineLength = lineLength;
invalidate();
}
public float getRadius() {
return mRadius;
}
public void setRadius(float radius) {
mRadius = radius;
invalidate();
}
public Paint getPaint() {
return mPaint;
}
public void setPaint(Paint paint) {
mPaint = paint;
}
public Bitmap getCacheBitmap() {
return mCacheBitmap;
}
public void setCacheBitmap(Bitmap cacheBitmap) {
mCacheBitmap = cacheBitmap;
}
public Canvas getCacheCanvas() {
return mCacheCanvas;
}
public void setCacheCanvas(Canvas cacheCanvas) {
mCacheCanvas = cacheCanvas;
}
public boolean isFirstAnim() {
return isFirstAnim;
}
private void setIsFirstAnim(boolean isFirstAnim) {
this.isFirstAnim = isFirstAnim;
}
public float getMinRadius() {
return minRadius;
}
public void setMinRadius(float minRadius) {
this.minRadius = minRadius;
}
public float getMaxRadius() {
return maxRadius;
}
public void setMaxRadius(float maxRadius) {
this.maxRadius = maxRadius;
}
public boolean isFinished() {
return isFinished;
}
private void setIsFinished(boolean isFinished) {
this.isFinished = isFinished;
}
}
就这样,一个简单的小动画就做好啦,但是,我发现这个动画做啦大量的运算和重绘,性能不怎么好,希望有人可以指导一下我该怎么样做出优秀的动画,而且对于5个光线的伸缩动画我也实现不了,希望有人可以帮助我。
最后,我发现自定义个性能好有漂亮的控件真的很难,而且对数学的要求也很高,我还有很多地方要努力,加油!
完整源码地址:https://github.com/wuapnjie/SunnyLoad
點擊查看更多內容
為 TA 點贊
評論
評論
共同學習,寫下你的評論
評論加載中...
作者其他優質文章
正在加載中
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦



