自定义动画(仿Win10加载动画)——优化

来源:互联网 发布:手机维修自学软件 编辑:程序博客网 时间:2024/06/05 19:18

此为前文章写的仿Win10加载动画的优化版


源代码

已更新到github

优化分析

原生 自定义高仿(v1版) 这里写图片描述 这里写图片描述

一直觉得自己写的与原生的有差别,经过仔细对比观察,发现:

  1. 原生的圆点出发位置不是都在底部,而是第一个在底部,后面的紧接着前面一个,像球在管子里一样
  2. 圆点结束的位置就是该圆点开始的位置
  3. 经过比对,发现一个周期的时间是7500ms,非7000ms

经过优化后的对比:

原生 自定义高仿(v1.1版) 这里写图片描述 这里写图片描述

优化后的时间校正图:
这里写图片描述

核心代码

不可否认,原生更加自然。为了自然,尝试了过去掉顶部的两段匀速运动,直接用四段三阶贝塞尔曲线,原理上是行的通的。但测试过N多参数,后来还是觉得不够自然,放弃了。最终还是选择原来的方法。

起始角度的计算
第一个圆点在最底部,第二个与第一个相差一个圆点对旋转中心所占的角度,后面也是圆点也是一样,与前一个圆点相差此角度。此角度可通过圆点半径与轨迹半径计算:

// 计算圆点对旋转中心所占的角度 float trackR = halfSize - dotR; dotDegree = (float) Math.toDegrees(2 * Math.asin(dotR / trackR));

参数与之前相比,有了很大调整(最主要的就是参数,调了无数遍才调出来……):

/** * 创建动画 * * @param view 需执行的控件 * @param index 该控件执行的顺序 * @return 该控件的动画 */private Animator createViewAnim(final View view, final int index) {    long duration = 7500; // 一个周期(2圈)一共运行7500ms,固定值    // 最小执行单位时间    final float minRunUnit = duration / 100f;    // 最小执行单位时间所占总时间的比例    double minRunPer = minRunUnit / duration;    // 在插值器中实际值(Y坐标值),共8组    final double[] trueRunInOne = new double[]{            0,            0,            160 / 720d,            190 / 720d,            360 / 720d,            520 / 720d,            550 / 720d,            1    };    // 动画开始的时间比偏移量。剩下的时间均摊到每个圆点上    final float offset = (float) (index * (100 - 86) * minRunPer / (mDotViews.length - 1));    // 在差值器中理论值(X坐标值),与realRunInOne对应    final double[] rawRunInOne = new double[]{            0,            offset + 0,            offset + 11 * minRunPer,            offset + 32 * minRunPer,            offset + 43 * minRunPer,            offset + 54 * minRunPer,            offset + 75 * minRunPer,            offset + 86 * minRunPer    };    logI("minRunUnit=%f, minRunPer=%f, offset=%f", minRunUnit, minRunPer, offset);    // 各贝塞尔曲线控制点的Y坐标    final float p1_2 = calculateLineY(rawRunInOne[2], trueRunInOne[2], rawRunInOne[3], trueRunInOne[3], rawRunInOne[1]);    final float p1_4 = calculateLineY(rawRunInOne[2], trueRunInOne[2], rawRunInOne[3], trueRunInOne[3], rawRunInOne[4]);    final float p1_5 = calculateLineY(rawRunInOne[5], trueRunInOne[5], rawRunInOne[6], trueRunInOne[6], rawRunInOne[4]);    final float p1_7 = calculateLineY(rawRunInOne[5], trueRunInOne[5], rawRunInOne[6], trueRunInOne[6], rawRunInOne[7]);    // A 创建属性动画:绕着中心点旋转2圈    ObjectAnimator objAnim = ObjectAnimator.ofFloat(view, "rotation", -dotDegree * index, 720 - dotDegree * index);    // B 设置一个周期执行的时间    objAnim.setDuration(duration);    // C 设置重复执行的次数:无限次重复执行下去    objAnim.setRepeatCount(ValueAnimator.INFINITE);    // D 设置差值器    objAnim.setInterpolator(new TimeInterpolator() {        @Override        public float getInterpolation(float input) {            if (input < rawRunInOne[1]) {                // 1 等待开始                if (view.getVisibility() != INVISIBLE) {                    view.setVisibility(INVISIBLE);                }                return 0;            } else if (input < rawRunInOne[2]) {                if (view.getVisibility() != VISIBLE) {                    view.setVisibility(VISIBLE);                }                // 2 底部 → 左上角:贝赛尔曲线1                // 先转换成[0, 1]范围                input = calculateNewPercent(rawRunInOne[1], rawRunInOne[2], 0, 1, input);                return calculateBezierQuadratic(trueRunInOne[1], p1_2, trueRunInOne[2], input);            } else if (input < rawRunInOne[3]) {                // 3 左上角 → 顶部:直线                return calculateLineY(rawRunInOne[2], trueRunInOne[2], rawRunInOne[3], trueRunInOne[3], input);            } else if (input < rawRunInOne[4]) {                // 4 顶部 → 底部:贝赛尔曲线2                input = calculateNewPercent(rawRunInOne[3], rawRunInOne[4], 0, 1, input);                return calculateBezierQuadratic(trueRunInOne[3], p1_4, trueRunInOne[4], input);            } else if (input < rawRunInOne[5]) {                // 5 底部 → 左上角:贝赛尔曲线3                input = calculateNewPercent(rawRunInOne[4], rawRunInOne[5], 0, 1, input);                return calculateBezierQuadratic(trueRunInOne[4], p1_5, trueRunInOne[5], input);            } else if (input < rawRunInOne[6]) {                // 6 左上角 → 顶部:直线                return calculateLineY(rawRunInOne[5], trueRunInOne[5], rawRunInOne[6], trueRunInOne[6], input);            } else if (input < rawRunInOne[7]) {                // 7 顶部 → 底部:贝赛尔曲线4                input = calculateNewPercent(rawRunInOne[6], rawRunInOne[7], 0, 1, input);                return calculateBezierQuadratic(trueRunInOne[6], p1_7, trueRunInOne[7], input);            } else {                // 8 消失                if (view.getVisibility() != INVISIBLE) {                    view.setVisibility(INVISIBLE);                }                return 1;            }        }    });    return objAnim;}
0 0
原创粉丝点击