Android PathMeasure实现粒子下落效果

原创
2020/11/14 13:04
阅读数 718

一、效果图

二、代码实现

public class ObjectFallingView extends View implements ValueAnimator.AnimatorUpdateListener {

    private Paint mPaint = null;
    private int mWidth;
    private int mHeight;

    private List<ObjectItem> mObjectItems = new ArrayList<>();
    private long mDuration = 1200;
    private int maxGroup = 3;
    private ValueAnimator mUpdateFrameAniator;
    private int mObjectCounter = 0;

    private long fraquceDuration = 125;
    private long lastIncrementTime = 0;
    private boolean isPlaying = false;

    public ObjectFallingView(Context context) {
        this(context, null);
    }

    public ObjectFallingView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ObjectFallingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setClickable(true);
        setFocusable(true);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (heightMode != MeasureSpec.EXACTLY) {
            heightSize = getPaddingTop() + getPaddingBottom() + 320;
        }

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);


        if (widthMode != MeasureSpec.EXACTLY) {
            widthSize = getPaddingTop() + getPaddingBottom() + 320;
        }

        setMeasuredDimension(widthSize, heightSize);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        for (int i = 0; i < mObjectItems.size(); i++) {
            ObjectItem objectItem = mObjectItems.get(i);
            if (!objectItem.isRunning) {
                continue;
            }
            objectItem.draw(canvas, mPaint);
        }

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
    }


    public void addObjectItem(final int index) {

        final Path path = new Path();
        final PathMeasure pathMeasure = new PathMeasure();
        final ObjectItem objectItem = new ObjectItem();

        //绘制三阶贝塞尔曲线 起点位置
        PointF startPoint = new PointF();
        //贝塞尔结束点
        PointF endPoint = new PointF();

        //贝塞尔控制点1
        PointF control1 = new PointF();
        //贝塞尔控制点2
        PointF control2 = new PointF();

        initControlPoint(startPoint, control1, control2, endPoint, index);

        path.moveTo(startPoint.x, startPoint.y);
      //  path.cubicTo(control1.x, control1.y, control2.x, control2.y, endPoint.x, endPoint.y);
        path.quadTo(control1.x, control1.y, endPoint.x, endPoint.y);
        pathMeasure.setPath(path, false);

        objectItem.index = index;
        objectItem.pathMeasure = pathMeasure;
        objectItem.startTime = System.currentTimeMillis();
        objectItem.endTime = objectItem.startTime + mDuration;
        objectItem.startAlpha = (int) (145 + (100) * Math.random());
        objectItem.color = argb(objectItem.startAlpha, 255, 255, 255);
        objectItem.angel = (float) (90 * Math.random());
        objectItem.size = (int) dp2px(getContext(), (float) (7 + 8 * Math.random()));
        mObjectItems.add(objectItem);
    }

    public static int argb(
            @IntRange(from = 0, to = 255) int alpha,
            @IntRange(from = 0, to = 255) int red,
            @IntRange(from = 0, to = 255) int green,
            @IntRange(from = 0, to = 255) int blue) {
        return (alpha << 24) | (red << 16) | (green << 8) | blue;
    }

    /**
     * 设置控制点
     *
     * @param control1
     * @param control2
     */
    private void initControlPoint(PointF startPoint, PointF control1, PointF control2, PointF endPoint, int index) {

        int delta = (index % maxGroup);

        float padding = dp2px(getContext(), 35);

        float contentWidth = mWidth - 2*padding;
        float average = ((contentWidth) * 1.0f / maxGroup);


        startPoint.x = padding  +  (float) (contentWidth * Math.random());
        startPoint.y = 0;


        control1.x = padding / 2 + (float) (average * (delta) + average * Math.random());
        control1.y = (float) (Math.random() * mHeight / 2);
        if(control1.y ==0){
            control1.y = mHeight/2;
        }

        control2.x = padding / 2 + (float) (average * (delta) + average * Math.random());
        control2.y =  (float) (Math.random() * mHeight / 2 + control1.y);

        if(control2.y==control1.y){
            control2.y = 2*control1.y;
        }

        endPoint.x = padding / 2 +  (float) (average * (delta) + average * Math.random());
        endPoint.y = mHeight;

    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {


        if (isPlaying) {
            int count = mObjectItems.size();
            long dx = System.currentTimeMillis() - lastIncrementTime;
            if (count < 40 && dx > fraquceDuration) {
                int num = mObjectCounter%3;

                while(num>=0){
                    addObjectItem(mObjectCounter);
                    mObjectCounter++;
                    num--;
                }

                if (mObjectCounter >= Integer.MAX_VALUE) {
                    mObjectCounter = 0;
                }
                lastIncrementTime = System.currentTimeMillis();
            }
        }


        Iterator<ObjectItem> iterator = mObjectItems.iterator();
        do {
            if (iterator == null || !iterator.hasNext()) break;
            ObjectItem objectItem = iterator.next();
            PathMeasure pathMeasure = objectItem.pathMeasure;
            float pathLength = pathMeasure.getLength();

            float currentTime = (System.currentTimeMillis() - objectItem.startTime);
            float fraction = currentTime / mDuration;
            if (fraction <= 1) {

                float[] pos = new float[2];
                pathMeasure.getPosTan(fraction * pathLength, pos, null);
                objectItem.x = pos[0];
                objectItem.y = pos[1];
                if (objectItem.y == 0) {
                    objectItem.isRunning = false;
                } else {
                    objectItem.isRunning = true;
                }
                objectItem.fraction = fraction;

            } else {
                objectItem.isRunning = false;
                iterator.remove();
            }

        } while (true);

        invalidate();

        if (!isPlaying && mObjectItems.isEmpty()) {
            cancelUpdateFrameAnimator();
        }
    }

    public void stopPlay() {
        isPlaying = false;
    }


    public static float dp2px(Context context, float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                context.getResources().getDisplayMetrics());
    }


    public void startPlay() {

        cancelUpdateFrameAnimator();

        isPlaying = true;

        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
        //先加速后减速
        animator.setInterpolator(new LinearInterpolator());
        //动画的长短来控制速率
        animator.setDuration(50);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setRepeatMode(ValueAnimator.RESTART);
        animator.addUpdateListener(this);

        animator.start();
        mUpdateFrameAniator = animator;
    }

    private void cancelUpdateFrameAnimator() {
        if (mUpdateFrameAniator != null) {
            mUpdateFrameAniator.removeAllUpdateListeners();
            mUpdateFrameAniator.cancel();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        stopPlay();
    }

    public boolean isPlaying() {
        return isPlaying;
    }


    static class ObjectItem {

        public int index;
        public float fraction;
        public float x;
        public float y;
        public boolean isRunning;
        public long startTime = System.currentTimeMillis();
        public long endTime = System.currentTimeMillis();
        public float angel;
        public int startAlpha;
        private PathMeasure pathMeasure;
        private int color = Color.WHITE;
        private int size = 1;

        public void draw(Canvas canvas, Paint paint) {
            paint.setColor(color);
            paint.setAlpha((int) ((startAlpha * (1.0f - this.fraction)) + 20*fraction));

            RectF rectF = new RectF();
            rectF.left = -size / 2;
            rectF.top = -size / 2;
            rectF.right = +size / 2;
            rectF.bottom = +size / 2;
            canvas.save();
            canvas.translate(x, y);
            canvas.rotate(angel);
            canvas.drawRect(rectF, paint);
            canvas.restore();
        }
    }

}

 

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部