码农翻身

Android 贝塞尔曲线的实现(二)

- by MRyan, 2020-03-10


上文说了什么是贝塞尔曲线,和如何利用AndroidApi绘制出贝塞尔曲线。

那么这节我们就来实现几个进阶的贝塞尔曲线动画。

1.流动的波浪


演示效果图:

在这里插入图片描述

实现思路:

将屏幕宽度分为4份,其中1/2为一个波长,一个波长(0-》1/2)必有一个波峰,一个波谷。()一个波长利用贝塞尔曲线绘制)从屏幕左侧外面多出3/2个波长绘制,设置动画(移动控制贝塞尔曲线的固定点和控制点达到平移波浪的效果)无限次循环,开启动画。

在这里插入图片描述

代码实现:

绘制封闭波浪

    @Override

        protected void onDraw(Canvas canvas) {

            Path path = new Path();

            int itemWidth = weight / 2;

            path.moveTo(-3 * itemWidth, height / 2);

            for (int i = -3; i < 2; i++) {

                int controlX = i * itemWidth;

                path.quadTo(itemWidth / 2 + controlX + offset, getWaveHeight(i), controlX + itemWidth + offset, height / 2);

            }

            path.lineTo(weight, height);

            path.lineTo(0, height);

            path.close();//封闭区间

            canvas.drawPath(path, mWaveViewPaint);//画路径

        }

      //根据给定的i 确定是波峰还是波谷

        private int getWaveHeight(int count) {

            if (count % 2 == 0) {

                return height / 2 - 170;

            }

            return height / 2 + 170;

        }

 开启动画


    /**

         * 开启动画

         */

        private void start() {

            isStart = true;

            if(onStartListener!=null){

                onStartListener.onIsStart(true);

            }

            animator = ValueAnimator.ofFloat(0, weight);

            animator.setInterpolator(new LinearInterpolator());

            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                @Override

                public void onAnimationUpdate(ValueAnimator animation) {

                    float animatorValue = (float) animation.getAnimatedValue();

                    offset = animatorValue;

                    postInvalidate();

                }

            });

            animator.setDuration(1500);

            animator.setRepeatCount(ValueAnimator.INFINITE);//重复执行 无限次数

            animator.start();

        }

暴露接口


    public interface onStartListener {

            void onIsStart(boolean isStart);

        }

        public onStartListener onStartListener;

        public void setOnStart(onStartListener onStartListener) {

            this.onStartListener = onStartListener;

        }

        /**

         * 关闭动画

         */

        public void stop() {

            isStart = false;

            if(onStartListener!=null){

                onStartListener.onIsStart(false);

            }

            animator.cancel();

        }

        public void isStart(){

            if(isStart){

                stop();

            }else{

                start();

            }

        }

2.下落的弹球


演示效果图:

在这里插入图片描述

实现原理:

绘制小球及绳子(绳子利用贝塞尔曲线绘制)准备工作,小球加速下降,降到和绳子平行时,绳子随小球一起下降,下降到最低点,小球反弹绳子接着也反弹减速向上,小球脱离绳子后绳子回到初始位置。这里无非就是利用AccelerateInterpolator()控制加速运动,DecelerateInterpolator()控制减速运动。修改贝塞尔曲线的控制点达到修改绳子位置,没什么说的十分简单。

代码:

绘制


    @Override

        protected void onDraw(Canvas canvas) {

            canvas.drawCircle(30, height / 2+500, 25, mLineCirclePaint);//绳左球

            canvas.drawCircle(width - 30, height / 2+500, 25, mLineCirclePaint);//绳右球

            canvas.drawCircle(width / 2, height / 2+500 + mballControlly, 60, mballPaint);//画下落的球

            /*贝塞尔曲线绘制绳*/

            mPath.reset();

            mPath.moveTo(23, height / 2+500);

            mPath.quadTo(mLineControllx, height / 2+500 + mLineControlly, width - 23, height / 2+500);

            canvas.drawPath(mPath, mLinePaint);

        }

动画

      /**

         * 运行动画

         */

        public void startAni() {

            //加速插值器

            downAnimator = ValueAnimator.ofFloat(mballMaxy, mballMiny);

            //球加速下降 当球下降接触到绳时 绳也下降

            downAnimator.setDuration(1500);

            downAnimator.setInterpolator(new AccelerateInterpolator());

            downAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                @Override

                public void onAnimationUpdate(ValueAnimator animation) {

                    mballControlly = (float) animation.getAnimatedValue();

                    if (height / 2 + mballControlly >= height / 2) {

                        mLineControlly = (float) ((float) (animation.getAnimatedValue()) * 2.5);

                    }

                    postInvalidate();

                }

            });

            downAnimator.start();

            downAnimator.addListener(new AnimatorListenerAdapter() {

                @Override

                public void onAnimationEnd(Animator animation) {

                    startUpAnimator();

                }

            });

            //球和绳减速上升 当绳超过反向mballControlly 时球绳分离

            //减速插值器

            upAnimator = ValueAnimator.ofFloat(mballMiny, mballMaxy);//300->-900

            upAnimator.setDuration((long) (1500*0.7));

            upAnimator.setInterpolator(new DecelerateInterpolator());

            upAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                @Override

                public void onAnimationUpdate(ValueAnimator animation) {

                    float animatedValue = (float) animation.getAnimatedValue();

                    mballControlly = animatedValue;

                    if(mballControlly+height/2>=height/2+mballMaxy*0.4&&mballControlly<150){//小球先上升后绳在上升

                        mLineControlly=animatedValue;

                    }/*else if(mballControlly+height/2<height/2+mballMaxy/3) {

                        mLineControlly = -animatedValue / 2;

                    }*/

                    if(animatedValue<=-898){//执行完毕 绳回到中心点

                        mLineControlly=0;

                    }

                    postInvalidate();

                }

            });

            upAnimator.addListener(new AnimatorListenerAdapter() {

                @Override

                public void onAnimationEnd(Animator animation) {

                    startDownAnimator();

                }

            });

        }

        /**

         * 开始反弹

         */

        public void startUpAnimator() {

            if (upAnimator != null && upAnimator.getValues() != null && upAnimator.getValues().length > 0 && !upAnimator.isRunning()) {

                upAnimator.start();

            }

        }

        /**

         * 开始下降

         */

        public void startDownAnimator() {

            if (downAnimator != null && downAnimator.getValues() != null && downAnimator.getValues().length > 0 && !downAnimator.isRunning()) {

                downAnimator.start();

            }

        }

到这里就结束了

附上项目源码


项目源码

作者:MRyan


本文采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
转载时请注明本文出处及文章链接。本文链接:https://wormholestack.com/archives/177/
2025 © MRyan 82 ms