您的位置:首页 > 其它

酷炫动画----Twitter like(心形)动画实现及分析

2015-12-30 11:34 441 查看
最近在github很火的一个动画效果,代码写得还是不错的,拿来学习下,做个总结。

先来看下最终的实现效果:



最简单直接的实现方式就是使用Frame Animiations,但是性能和可扩张性都不高,这里通过更复杂的一个方式:自己绘制图形并且利用ObjectAnimator来执行动画。

具体实现

整个动画控件由一个FrameLayout 组成,这个FrameLayout包含三个子View,

CircleView ,显示五角星下面的圆形动画

ImageView ,是一个五角星

DotsView 代表最外边飞动的小圆点

1. CircleView



仔细观察,整个变化过程由三部分组成:1. 一个实心圆半径(这个圆形的大小)由小变大,2. 在大小变化的过程中,颜色也在同时变化,3. 实心圆变到一个定大小时,开始出现一个内径圆,是白色的,然后半径由小变大,最终将黄色的圆覆盖掉。这里感觉比较难实现的是内心圆的动画。

具体的绘制必然是在onDraw()方法中

Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
tempCanvas.drawColor(0xffffff, PorterDuff.Mode.CLEAR);
tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, outerCircleRadiusProgress * maxCircleSize, circlePaint);
tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, innerCircleRadiusProgress * maxCircleSize, maskPaint);
canvas.drawBitmap(tempBitmap, 0, 0, null);}


首先利用绘制颜色(使用Clear模式)将整个画布做一个初始化,然后根据具体的进度(outerCircleRadiusProgress,innerCircleRadiusProgress)绘制内径圆和外面的圆

内径圆使用的mash paint如下面定义:

maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));


这样外面的圆就会在内部创建一个透明的洞,也即是内径圆。

里面的tempCanvas在如下代码创建:

Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
maxCircleSize = w / 2;
tempBitmap = Bitmap.createBitmap(getWidth(), getWidth(), Bitmap.Config.ARGB_8888);
tempCanvas = new Canvas(tempBitmap);
}


这样可以避免内径圆显示window color。

那么颜色是怎么变化的呢?是通过ArgbEvaluator 这个类来根据给定的比率在两个颜色值之间变化。

private void updateCircleColor() {
float colorProgress = (float) Utils.clamp(outerCircleRadiusProgress, 0.5, 1);
colorProgress = (float) Utils.mapValueFromRangeToRange(colorProgress, 0.5f, 1f, 0f, 1f);
this.circlePaint.setColor((Integer) argbEvaluator.evaluate(colorProgress, START_COLOR, END_COLOR));
}


CircleView主要的代码就分析完了,下面来看DotsView。


2. DotsView



这个View来显示外围的小圆点动画,同CircleView 一样,也是在onDraw()绘画的:


@Override
protected void onDraw(Canvas canvas) {
drawOuterDotsFrame(canvas);
drawInnerDotsFrame(canvas);
}

private void drawOuterDotsFrame(Canvas canvas) {
for (int i = 0; i < DOTS_COUNT; i++) {
int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
canvas.drawCircle(cX, cY, currentDotSize1, circlePaints[i % circlePaints.length]);
}

private void drawInnerDotsFrame(Canvas canvas) {
for (int i = 0; i < DOTS_COUNT; i++) {
int cX = (int) (centerX + currentRadius2 * Math.cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));
int cY = (int) (centerY + currentRadius2 * Math.sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));
canvas.drawCircle(cX, cY, currentDotSize2, circlePaints[(i + 1) % circlePaints.length]);
}
}


可以看到通过循环创建了一些小圆点,每隔OUTER_DOTS_POSITION_ANGLE (51)绘制一组小圆点。 每个圆点的颜色变化是通过paint.setColor控制的:

private void updateDotsPaints() {
if (currentProgress < 0.5f) {
float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0f, 0.5f, 0, 1f);
circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2));
circlePaints[1].set
4000
Color((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3));
circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4));
circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1));
} else {
float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, 0, 1f);
circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3));
circlePaints[1].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4));
circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1));
circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2));
}}


这里的颜色变化也是使用ArgbEvaluator 来根据progress生成具体的颜色值。

3. LikeButtonView

这个view继承了FrameLayout,将CircleView, ImageView 和 DotsView组合到一起

<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<frogermcs.io.likeanimation.DotsView
android:id="@+id/vDotsView"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"/>

<frogermcs.io.likeanimation.CircleView
android:id="@+id/vCircle"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center"/>

<ImageView
android:id="@+id/ivStar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_star_rate_off"/>

</merge>


这里使用Merger标签来去除多余的层级嵌套,LikeButtonView 本身就是一个FrameLayout ,没有必要使用两次。

最后完整的动画效果就是将上面的动画效果组合到一起,放入到AnimatorSet来一起播放。

@Override
public void onClick(View v) {
//...

animatorSet = new AnimatorSet();

ObjectAnimator outerCircleAnimator = ObjectAnimator.ofFloat(vCircle, CircleView.OUTER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f);
outerCircleAnimator.setDuration(250);
outerCircleAnimator.setInterpolator(DECCELERATE_INTERPOLATOR);

ObjectAnimator innerCircleAnimator = ObjectAnimator.ofFloat(vCircle, CircleView.INNER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f);
innerCircleAnimator.setDuration(200);
innerCircleAnimator.setStartDelay(200);
innerCircleAnimator.setInterpolator(DECCELERATE_INTERPOLATOR);

ObjectAnimator starScaleYAnimator = ObjectAnimator.ofFloat(ivStar, ImageView.SCALE_Y, 0.2f, 1f);
starScaleYAnimator.setDuration(350);
starScaleYAnimator.setStartDelay(250);
starScaleYAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR);

ObjectAnimator starScaleXAnimator = ObjectAnimator.ofFloat(ivStar, ImageView.SCALE_X, 0.2f, 1f);
starScaleXAnimator.setDuration(350);
starScaleXAnimator.setStartDelay(250);
starScaleXAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR);

ObjectAnimator dotsAnimator = ObjectAnimator.ofFloat(vDotsView, DotsView.DOTS_PROGRESS, 0, 1f);
dotsAnimator.setDuration(900);
dotsAnimator.setStartDelay(50);
dotsAnimator.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR);

animatorSet.playTogether(
outerCircleAnimator,
innerCircleAnimator,
starScaleYAnimator,
starScaleXAnimator,
dotsAnimator
);

//...

animatorSet.start();
}


基本的实现框架就是这样的,具体完整的代码见:

https://github.com/frogermcs/LikeAnimation/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  心形动画 控件