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


最近在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].setColor((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/

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当初装sublime的时候在csdn上没找到比较好的插件合集 费了一点功夫自己整合了一下 该集合包括了多种常用插件emmet jsFormat git相关 以及css和php js jquery html5等语言的支持增强插件等 已安装utf8和gbk插件 完美支持中文输入 包含codeIntel和cTags Filediff 侧边栏增强等一系列插件方便开发使用 此外收集了网上的5套主题 多达40几种配色方案 支持自定义外观 默认主题为扁平化flatland 预览:http://lucifr.com/2013/04/12/flatland-theme-for-sublime-text-2/ 请阅读链接中的主题替换方法 以便之后完整替换主题 安装方法: 1.sublime text3版本: 打开sublime text3 依次点击工具栏中的preference->browse packages 到一个类似C:\Users\Adiministor\AppData\Roaming\Sublime Text 3\Packages 的目录 转到向上一级 C:\Users\Adiministor\AppData\Roaming\Sublime Text 3 下 把压缩包中的4个文件夹解压到这个路径覆盖即可 若有自己已定义的内容请先备份 之后再把自己的东西覆盖回去 2.sublime text2版本: 测试过sublime text2 同样通过上述方法可以到相应的路径中 (可能是C:\Users\Adiministor\AppData\Roaming\Sublime Text 2) 然后把压缩包中文件夹解压到C:\Users\Adiministor\AppData\Roaming\Sublime Text 2覆盖 接下来 最重要的一步!! 是到 C:\Users\Adiministor\AppData\Roaming\Sublime Text 2\Packages\User 这个路径下 把Default (Windows).sublime-keymap这个文件的内容全部删掉 或者只保留[]这两个符号 (这里面定义了一些系统默认快捷键 可能是sublime text2只支持32位? 如果不删除的话会导致st2无法启动) 我还是很懒的 喜欢把一些插件集合起来用 但是具体还是要看个人的需求吧 通过package control有太多的插件可供安装 希望这个插件集合能给大家带来一点小便利 方便刚装sublime text的用户哈

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值