三阶贝塞尔曲线实现直播间刷礼物功能和波浪的效果

该文章展示了在Android中如何使用三阶贝塞尔曲线来创建图像随机曲线飘动的效果,以及如何通过Path和Paint实现水波般的波动动画。代码示例包括贝塞尔曲线的计算和应用,以及View的重绘和动画监听处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

效果:
在这里插入图片描述

  • 三阶贝塞尔曲线执行原理过程

在这里插入图片描述
P0点是起始点 , P3 点是终止点 , P 1 和 P 2 点是控制点 ;

  • 三阶贝塞尔曲控制点计算公式
    在这里插入图片描述
    根据比例 , 绘制出 P0与 P2之间的二阶贝塞尔曲线 , 以 P1 为控制点 , 绘制出直线 AB ;然后 绘制 P1与 P3之间的二阶贝塞尔曲线 , 以 P2为控制点 , 绘制出直线 BC;最后 计算A到C之间的二阶贝塞尔曲线 , 以 B 点作为 控制点

一、实现图片由下往上随机曲线飘动。

public class FloatAnimView extends FrameLayout {

    public FloatAnimView(@NonNull Context context) {
        this(context, null);
    }

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

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


    /**
     * 添加view
     */
    public void addItemView() {
        ImageView imageView = new ImageView(getContext());
        imageView.setImageResource(R.mipmap.icon_sx);
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(10, 10);
        layoutParams.gravity = Gravity.BOTTOM;
        addView(imageView, layoutParams);
        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) imageView.getLayoutParams();
        marginLayoutParams.leftMargin = getMeasuredWidth() / 2;
        animIcon(imageView);
    }


    private int[] size = {20, 15, 16, 18, 19, 13, 12, 11, 8, 5, 6};

    /**
     * 开始动画
     *
     * @param animIcon
     */
    public void animIcon(ImageView animIcon) {
//        animIcon.setRotation(new Random().nextInt(360));

        /*随机显示图片的大小*/
        ValueAnimator animator = ValueAnimator.ofFloat(10, size[new Random().nextInt(10)]);
        animator.setDuration(500);
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(@NonNull ValueAnimator animation) {
                float f = (float) animation.getAnimatedValue();
                animIcon.setScaleX(f);
                animIcon.setScaleY(f);
            }
        });

        PointF[] p = initPointF();
        Bezier bezier = new Bezier(p[1],p[2],p[3]);
        ValueAnimator valueAnimator = ValueAnimator.ofObject(bezier, p[0], p[3]);
        valueAnimator.setDuration(1500);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(@NonNull ValueAnimator animation) {
                PointF pointF = (PointF) animation.getAnimatedValue();
                animIcon.setX(pointF.x);
                animIcon.setY(pointF.y);
            }
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                FloatAnimView.this.removeView(animIcon);
                valueAnimator.reverse();
            }
        });
        valueAnimator.start();
    }


    private PointF p0, p1, p2, p3;

    public PointF[] initPointF() {
        PointF[] pointFS = new PointF[4];
        p0 = new PointF(getMeasuredWidth() / 2, getMeasuredHeight());
        PointF p1 = new PointF(new Random().nextInt(getMeasuredWidth()), getMeasuredHeight() / 2);
        PointF p2 = new PointF(new Random().nextInt(getMeasuredWidth()), getMeasuredHeight() / 2+new Random().nextInt( getMeasuredHeight() / 2));
        PointF p3 = new PointF(new Random().nextInt(getMeasuredWidth()), 0);

        pointFS[0] = p0;
        pointFS[1] = p1;
        pointFS[2] = p2;
        pointFS[3] = p3;

        return pointFS;
    }


    public class Bezier implements TypeEvaluator<PointF> {

        public PointF pointF1, pointF2,pointF3;

        public Bezier(PointF pointF1, PointF pointF2,PointF pointF3) {
            this.pointF1 = pointF1;
            this.pointF2 = pointF2;
            this.pointF3 = pointF3;
        }


        @Override
        public PointF evaluate(float t, PointF startValue, PointF endValue) {
            PointF pointF = new PointF();
            pointF.x = (1 - t) * (1 - t) * (1 - t) * p0.x
                    +
                    3 * (1 - t) * (1 - t) * t * pointF1.x
                    +
                    3 * (1 - t) * t * t * pointF2.x
                    +
                    t * t * t * pointF3.x;

            pointF.y = (1 - t) * (1 - t) * (1 - t) * p0.y
                    +
                    3 * (1 - t) * (1 - t) * t * pointF1.y
                    +
                    3 * (1 - t) * t * t * pointF2.y
                    +
                    t * t * t * pointF3.y;

            return pointF;
        }
    }

二、实现水波一样的 浪呀浪。

public class WaterWaveView extends View {

private Path path = new Path();
private Paint paint ;
int centerLineY ;
int arcY = 50; //震弧
int arcSpeed = 300;//控制点距
int countPoint = 4;
int startPoint;

AccelerateInterpolator

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

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

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

public void init(){
    paint = new Paint(Paint.ANTI_ALIAS_FLAG) ;
    paint.setStyle(Paint.Style.FILL_AND_STROKE);
    paint.setStrokeWidth(10);
    paint.setColor(Color.argb(100,255,0,0));
    paint.setAntiAlias(true);
    paint.setDither(true);
}


public void setCenterLineY(int progress) {
    this.centerLineY = (int) (getMeasuredHeight() * (1 - progress * 1.0 / 100)) ;
    postInvalidate();
}

public void random(){
    path.reset();
    if (Math.abs(startPoint)>=600){
        startPoint = -5;
    }
    startPoint += -5;
    postInvalidate();
}


@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    centerLineY = getMeasuredHeight()/2 ;
}

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

    countPoint = (int) Math.ceil((getMeasuredWidth()+arcSpeed)*1.0 / arcSpeed);

    if (centerLineY>0){
        path.moveTo(0,centerLineY);
        for (int i = 0 ; i<= countPoint;i++){
            path.cubicTo(
                    i * arcSpeed+startPoint,centerLineY,
                    i * arcSpeed+arcSpeed/2+startPoint,centerLineY+((i % 2 == 0)?-arcY:arcY),
                    i * arcSpeed+arcSpeed+startPoint,centerLineY
            );
        }

        path.lineTo(getMeasuredWidth(), getMeasuredHeight());
        path.lineTo(0, getMeasuredHeight());
        path.close();

        canvas.drawPath(path,paint);

        random();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

将哥哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值