ui

Android UI 篇- 手势月亮 亮度动画

2020-05-05  本文已影响0人  trycatchx

Android UI 篇- 手势月亮动画

一、应用场景

二、流程分析

三、代码实现(文章末尾彩蛋)

一、应用场景

1.1、先上效果图
2020-05-03 01_00_28.gif

一个有创意的亮度动画,通过手势上下滑动控制手机屏幕亮度,动画从太阳(天亮了)变成月亮(夜黑了),非常漂亮屏幕亮度动画!

二、流程分析

2.1、先上渐变图分析:
渐变图1

变量之间的关系:OC ⊥ ABA,B两点是贝塞尔曲线定点,C点是贝塞尔曲线的控制点。OC = K * ABK 是一个经验值目前可以设置为0.43f(那么C点可以被解出来),这样的塞尔曲线的弧度比较美观,拟合圆的弧度。

1588493267013.jpg

A,B 两点同时出发,A->A',B->B'.(逆时针)

备注:另外一种思路实现通过两个圆去 DST_OUT,得出第一个圆剩余的部分也是月亮,但是第二个圆的圆心轨迹函数,和半径变化轨迹函数,想要画出漂亮的月亮,两个运动轨迹没有规律可言,也很难被求出,只能适用于画静态的月亮,画不了动态的月亮。

三、代码实现

3.1、画圆形
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 太阳/月亮 到光晕的间隔是两倍光晕的宽度
        int margin = mHaloHeight + mHaloWidth * 2;
*         //实际上太阳/月亮 具体宽高
        mLayer.set(margin, margin, w - margin, h - margin); 
        mCirclePath.reset();
        //取宽高中 最短的最为太阳的半径
        circleR = w > h ? (h - 2 * margin) / 2.0f : (w - 2 * margin) / 2.0f;
        //顺时钟画圆,圆的起始位置在右侧中间
        mCirclePath.addCircle(mLayer.centerX(), mLayer.centerY(), circleR, Path.Direction.CW);
        //把画好圆的 path 添加到 PathMeasure,待会可以被 getPosTan 使用
        mMeasure.setPath(mCirclePath, false); 
    }
3.2、求通过progressA、B定点
    /**
     * 分段函数
     *
     * @param progress
     * @param point
     */
    private void getBeginPoint(float progress, float[] point) {
        if (progress <= 0.5) {
            //A 定点 在0.5 progress 之前都是在第四
            mMeasure.getPosTan(mMeasure.getLength() * (-0.2f * progress + 0.1f), point, null);
        } else {
            mMeasure.getPosTan(mMeasure.getLength() * (-0.2f * progress + 1.1f), point, null);
        }
    }

    /**
     * 分段函数
     *
     * @param progress
     * @param point
     */
    private void getSecondPoint(float progress, float[] point) {
        if (progress <= 0.1) {
            mMeasure.getPosTan(mMeasure.getLength() * (-1.0f * progress + 0.1f), point, null);
        } else {
            mMeasure.getPosTan(mMeasure.getLength() * (-7.0f / 9.0f * progress + 9.7f / 9f), point, null);

        }
    }
3.2、求控制点 C
1588606424855.jpg

    private float[] getContrlPoint(float[] point1, float[] point2) {
        float centerX = mLayer.centerX();
        float centerY = mLayer.centerY();
        float diffDis = (float) Math.sqrt((point1[0] - point2[0]) * (point1[0] - point2[0]) + (point1[1] - point2[1]) * (point1[1] - point2[1]));
        //中垂线函数 y = kx+b 中的 k
        float k = (point1[0] - point2[0]) / (point2[1] - point1[1]);
        //中垂线函数 y = kx+b 中的 b
        float b = (point1[1] + point2[1]) / 2.0f - (point1[0] * point1[0] - point2[0] * point2[0]) / 2.0f / (point2[1] - point1[1]);
        float[] point = {0f, 0f};
        // cosα 的值
        float cosDegrees = (float) (1 / Math.sqrt(1 + k * k));
        if (k < 0) {
            //magicNum 为0.43
            point[0] = (point1[0] + point2[0]) / 2.0f - (cosDegrees * diffDis * magicNum); 
        } else if (k > 0) {
            if (point1[0] > centerX && point1[1] > centerY && point2[0] > centerX) {
                point[0] = (point1[0] + point2[0]) / 2.0f - (cosDegrees * diffDis * magicNum);
            } else {
                point[0] = (point1[0] + point2[0]) / 2.0f + (cosDegrees * diffDis * magicNum);
            }
        } else {
            point[0] = (point1[0] + point2[0]) / 2.0f;
        }

        point[1] = k * point[0] + b;
        return point;
    }
3.3、画 AB 向圆心弯曲的贝塞尔曲线
        //找到第一个定点
        getBeginPoint(progress, mBeginPoint);
        //找到第二个定点
        getSecondPoint(progress, mSecondPoint);


        mQuadPath.reset();
        mQuadPath.moveTo(mBeginPoint[0], mBeginPoint[1]);
        float[] begin = {mBeginPoint[0], mBeginPoint[1]};
        //找到拟合圆的贝赛尔曲线控制点
        float[] contrlPoint = getContrlPoint(begin, mSecondPoint);
        //画贝赛尔曲线
        mQuadPath.quadTo(contrlPoint[0], contrlPoint[1], mSecondPoint[0], mSecondPoint[1]);
3.4、画 AB 本身的圆弧
1588606677198.jpg
 private Pair<Float, Float> getAngle(float[] point1, float[] point2) {
        float centerX = mLayer.centerX();
        float centerY = mLayer.centerY();
        float diffY;
        float degrees1 = 0;
        float degrees2;

        float startAngle;
        float sweepAngle;
        if (point2[0] > centerX && point2[1] > centerY) {
            degrees1 = (float) Math.toDegrees(Math.asin((point2[1] - centerY) / circleR));
            degrees2 = (float) Math.toDegrees(Math.asin((point1[1] - centerY) / circleR));
            startAngle = degrees1;
            sweepAngle = degrees2 - degrees1;
        } else {
            if (point2[0] > centerX) { //一 象限
                if (point2[1] < centerY) {
                    diffY = centerY - point2[1];
                    degrees1 = (float) Math.toDegrees(Math.asin(diffY / circleR));
                }
            } else { // 2 3 象限
                if (point2[1] < centerY) {
                    diffY = centerY - point2[1];
                    degrees1 = 180 - (float) Math.toDegrees(Math.asin(diffY / circleR));
                } else {
                    diffY = point2[1] - centerY;
                    degrees1 = (float) Math.toDegrees(Math.asin(diffY / circleR)) + 180;
                }
            }
            degrees2 = (float) Math.toDegrees(Math.asin((centerY - point1[1]) / circleR));

            startAngle = 360 - degrees1;
            sweepAngle = degrees1 - degrees2;
        }

        return new Pair<>(startAngle, sweepAngle);
    }
3.5、画光晕
        //画光晕
        canvas.save();
        //画布平移到中间,为了等下旋转使用
        canvas.translate(mLayer.centerX(), mLayer.centerY());
        //计算出当前进度需要画多少个光晕
        int count = mNumOfHalo - (int) (progress / mOneOFHaleProgress);
        float mHalfHaloWidth = mHaloWidth / 2;
        //开始画光晕
        for (int i = 0; i < count; i++) {
            canvas.drawRoundRect(new RectF(-mHalfHaloWidth,-mLayer.centerY(),mHalfHaloWidth,mHaloHeight - mLayer.centerY()), mHalfHaloWidth, mHalfHaloWidth, mPaint);
            canvas.rotate(mOneOFHaleDegrees);
        }
        canvas.restore();

彩蛋:到这基本就大功告成,虽然动画的细节非常多,但是我们还是把它给画出来了。给出了亮度的动画,这不还得要一个音量调节的动画?左边上下滑动调节亮度,右边上下滑动调节音量。(号称视频播放双动画)好的,这就给你献上,直接拿走不谢!

2020-05-05 18_09_07.gif

下面给出这个两个动画的开源代码,以及使用教程。github传送门。记得给个 Star (点个赞) 哦。

上一篇下一篇

猜你喜欢

热点阅读