酷炫动画----Twitter like(心形)动画实现及分析
2015-12-30 11:34
441 查看
最近在github很火的一个动画效果,代码写得还是不错的,拿来学习下,做个总结。
先来看下最终的实现效果:
![](https://img-blog.csdn.net/20151230113056268)
最简单直接的实现方式就是使用Frame Animiations,但是性能和可扩张性都不高,这里通过更复杂的一个方式:自己绘制图形并且利用ObjectAnimator来执行动画。
CircleView ,显示五角星下面的圆形动画
ImageView ,是一个五角星
DotsView 代表最外边飞动的小圆点
![](https://img-blog.csdn.net/20151230113204658)
仔细观察,整个变化过程由三部分组成:1. 一个实心圆半径(这个圆形的大小)由小变大,2. 在大小变化的过程中,颜色也在同时变化,3. 实心圆变到一个定大小时,开始出现一个内径圆,是白色的,然后半径由小变大,最终将黄色的圆覆盖掉。这里感觉比较难实现的是内心圆的动画。
具体的绘制必然是在onDraw()方法中
首先利用绘制颜色(使用Clear模式)将整个画布做一个初始化,然后根据具体的进度(outerCircleRadiusProgress,innerCircleRadiusProgress)绘制内径圆和外面的圆
内径圆使用的mash paint如下面定义:
这样外面的圆就会在内部创建一个透明的洞,也即是内径圆。
里面的tempCanvas在如下代码创建:
这样可以避免内径圆显示window color。
那么颜色是怎么变化的呢?是通过ArgbEvaluator 这个类来根据给定的比率在两个颜色值之间变化。
![](https://img-blog.csdn.net/20151230113305413)
可以看到通过循环创建了一些小圆点,每隔OUTER_DOTS_POSITION_ANGLE (51)绘制一组小圆点。 每个圆点的颜色变化是通过paint.setColor控制的:
这里的颜色变化也是使用ArgbEvaluator 来根据progress生成具体的颜色值。
这里使用Merger标签来去除多余的层级嵌套,LikeButtonView 本身就是一个FrameLayout ,没有必要使用两次。
最后完整的动画效果就是将上面的动画效果组合到一起,放入到AnimatorSet来一起播放。
基本的实现框架就是这样的,具体完整的代码见:
https://github.com/frogermcs/LikeAnimation/
先来看下最终的实现效果:
最简单直接的实现方式就是使用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/
相关文章推荐
- flex 控件的重要属性
- Delphi控件ListView的属性及使用方法详解
- web下载的ActiveX控件自动更新
- WinForm实现按名称递归查找控件的方法
- C#中父窗口和子窗口之间控件互操作实例
- Android编程之Button控件用法实例分析
- Android控件之CheckBox、RadioButton用法实例分析
- MFC中动态创建控件以及事件响应实现方法
- WinForm自定义函数FindControl实现按名称查找控件
- Android控件之ProgressBar用法实例分析
- WinForm拖拽控件生成副本的解决方法
- ASP.NET动态添加用户控件的方法
- ASP.NET的HtmlForm控件学习及Post与Get的区别概述
- WinForm实现移除控件某个事件的方法
- C#分屏控件用法实例
- 可以浮动某个物体的jquery控件用法实例
- jQuery操作表单常用控件方法小结
- C#的winform控件命名规范
- Jquery给基本控件的取值、赋值示例
- ASP.net 动态加载控件时一些问题的总结