美文网首页Android技术知识Android知识
Bezier曲线(N阶)实现水波纹晃动效果

Bezier曲线(N阶)实现水波纹晃动效果

作者: SharryChoo | 来源:发表于2018-01-29 16:56 被阅读126次

    效果展示

    波浪曲线效果展示.gif

    实现思路

    1. 只取一个波浪进行分析
      • 让起始点的位置在 View 左侧
      • 让他向右平移一个周期, 造成视觉上的波浪晃动效果
    2. 为了保证能够平移一个周期, 故 Bezier 曲线的最小控制点数量为 4 个
    3. 底部是个矩形, 故采用Path去链接即可
    4. 绘制静态曲线
    5. 使用属性动画根据需求去改变起始点的位置, 平移一个周期, 保证无缝衔接即可

    使用方式

            mWaveWrapView.setup(3, Color.parseColor("#67e1e9"), Color.parseColor("#cc9efe"),
                    Color.parseColor("#fd88b8"))
            mBtnStart.setOnClickListener { mWaveWrapView.start() }
            mBtnPause.setOnClickListener { mWaveWrapView.pause() }
            mBtnResume.setOnClickListener { mWaveWrapView.resume() }
            mBtnStop.setOnClickListener { mWaveWrapView.stop() }
    

    具体实现

    /**
     * Created by FrankChoo on 2017/11/7.
     * Email: frankchoochina@gmail.com
     * Version: 2.0
     * Description: 波浪曲线的自定义View
     */
    public class WaveWrapView extends View {
    
        private int mWaveCount = 3;
        private int mMinimumWaveHierarchy = 4;
        private Paint mWavePaint;
        private int[] mPaintColors;
        private PointF[] mCurrentStartPoints;// 当前起点的位置
        private float mWaveMaxHeightPercent = 0.5f;// 曲线占当前布局高度的最大百分比
        private float mWaveOffsetY;// Bezier 曲线控制点的偏移量
        private float mBaseStartX;// 起点位置
        private Path mWavePath;
        private AnimatorSet mWaveAnimSet;
    
        public WaveWrapView(Context context) {
            this(context, null);
        }
    
        public WaveWrapView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public WaveWrapView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        /**
         * 外界初始化曲线的方法
         * <p>
         *
         * @param waveCount
         * @param colors
         */
        public void setup(int waveCount, int... colors) {
            if (colors.length != waveCount) {
                throw new IllegalArgumentException("WaveWrapView.setup ->  colors length must equals wave count !");
            }
            mWaveCount = waveCount;
            mCurrentStartPoints = new PointF[mWaveCount];
            mPaintColors = new int[mWaveCount];
            for (int i = 0; i < mWaveCount; i++) {
                mPaintColors[i] = colors[i];
                mCurrentStartPoints[i] = new PointF();
            }
            // 初始化画笔
            mWavePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
            // 初始化Path路径
            mWavePath = new Path();
        }
    
        /**
         * 设置曲线占当前 View 最大高度的百分比
         *
         * @param percent
         */
        public void setWaveMaxHeightPercent(float percent) {
            this.mWaveMaxHeightPercent = percent;
        }
    
        public void start() {
            // 曲线动画集合
            if (mWaveAnimSet == null) {
                ValueAnimator[] valueAnimators = new ValueAnimator[mWaveCount];
                for (int i = 0; i < mWaveCount; i++) {
                    final int index = i;
                    // 计算一个周期的长度
                    float distanceX = getRight() - mBaseStartX;
                    float offsetX = distanceX / (mMinimumWaveHierarchy + index);
                    ValueAnimator animator = ValueAnimator.ofFloat(mBaseStartX, mBaseStartX + offsetX * 2);
                    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            mCurrentStartPoints[index].x = (float) animation.getAnimatedValue();
                            if (index != mWaveCount - 1) return;
                            invalidate();
                        }
                    });
                    animator.setRepeatCount(-1);
                    valueAnimators[index] = animator;
                }
                mWaveAnimSet = new AnimatorSet();
                mWaveAnimSet.playTogether(valueAnimators);
                mWaveAnimSet.setInterpolator(new LinearInterpolator());
                mWaveAnimSet.setDuration(1000);
            }
            mWaveAnimSet.start();
        }
    
        public void pause() {
            if (mWaveAnimSet != null && mWaveAnimSet.isStarted()) {
                mWaveAnimSet.pause();
            }
        }
    
        public void resume() {
            if (mWaveAnimSet != null && mWaveAnimSet.isStarted()) {
                mWaveAnimSet.resume();
            }
        }
    
        public void stop() {
            if (mWaveAnimSet != null && mWaveAnimSet.isStarted()) {
                mWaveAnimSet.cancel();
            }
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            // 控制点的偏移量, 当前 View 高度的十分之一
            mWaveOffsetY = getMeasuredHeight() / 10;
            // 起点的 X 坐标在当前 View 的左侧
            mBaseStartX = -getMeasuredWidth();
            // 给坐标点集赋值
            for (int i = 0; i < mWaveCount; i++) {
                mCurrentStartPoints[i].x = mBaseStartX;
                mCurrentStartPoints[i].y = (float) (getMeasuredHeight() + mWaveOffsetY -
                        (getMeasuredHeight() * mWaveMaxHeightPercent) / (1 + i * 0.5));
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            for (int i = 0; i < mWaveCount; i++) {
                mWavePaint.setColor(mPaintColors[i]);
                resetWavePath(mCurrentStartPoints[i], mMinimumWaveHierarchy + i);
                canvas.drawPath(mWavePath, mWavePaint);
            }
        }
    
        /**
         * 获取波浪路径
         *
         * @param curStart           当前起点坐标
         * @param bezierControlCount 曲线控制点的数量
         */
        private void resetWavePath(PointF curStart, int bezierControlCount) {
            mWavePath.reset();
            float distanceX = getRight() - mBaseStartX;
            float offsetX = distanceX / (bezierControlCount * 2);
            float marginX = offsetX * 2;
            // 曲线起点
            mWavePath.moveTo(curStart.x, curStart.y);
            for (int i = 0; i < bezierControlCount; i++) {
                mWavePath.quadTo(
                        curStart.x + offsetX + i * marginX,
                        curStart.y + (i % 2 == 0 ? mWaveOffsetY : -mWaveOffsetY),
                        curStart.x + marginX * (i + 1),
                        curStart.y
                );
            }
            // 闭合曲线
            mWavePath.lineTo(getRight(), getBottom());
            mWavePath.lineTo(0, getBottom());
            mWavePath.lineTo(0, curStart.y);
        }
    
    }
    

    相关文章

      网友评论

        本文标题:Bezier曲线(N阶)实现水波纹晃动效果

        本文链接:https://www.haomeiwen.com/subject/laxfzxtx.html