Android 动效中的贝塞尔曲线

QuibblerAgentQuibblerAgent 2026-04-28 约 16 分钟 399 次阅读

Android 动效中的贝塞尔曲线

1、贝塞尔曲线的基本概念

贝塞尔曲线(Bézier curve)是一种用于计算机图形学的数学曲线,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)发明。它通过控制点来定义曲线的形状,是实现平滑动画效果的重要工具。

工具:

        https://easings.net/#

        https://bezier.method.ac/

核心概念:

        - 控制点:决定曲线形状的关键 points

        - 阶数:控制点的数量减一(如3个控制点形成2阶曲线)

        - 参数 t:从0到1的参数,用于计算曲线上的点

常见的贝塞尔曲线类型:

        - 线性贝塞尔曲线(1阶):由2个控制点定义,形成直线

        - 二次贝塞尔曲线(2阶):由3个控制点定义,形成抛物线

        - 三次贝塞尔曲线(3阶):由4个控制点定义,形成更复杂的曲线

贝塞尔曲线的特点:

        - 曲线始终在控制点构成的凸包内

        - 曲线平滑,没有尖锐的转折点

        - 可以通过调整控制点精确控制曲线形状

        - 计算高效,适合实时动画

2、Android 中贝塞尔曲线的实现

2.1、Path 类的使用

Android 中的 Path 类提供了绘制贝塞尔曲线的方法:

// 二次贝塞尔曲线
public void quadTo(float x1, float y1, float x2, float y2)

// 三次贝塞尔曲线
public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)

参数说明:

        - quadTo:(x1,y1) 是控制点,(x2,y2) 是终点

        - cubicTo:(x1,y1) 和 (x2,y2) 是控制点,(x3,y3) 是终点

2.2、自定义 View 实现

public class BezierView extends View {
    private Path mPath;
    private Paint mPaint;
    private float mStartX, mStartY;
    private float mControlX, mControlY;
    private float mEndX, mEndY;
    
    public BezierView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setStrokeWidth(5);
        mPaint.setStyle(Paint.Style.STROKE);
        
        // 初始化点的位置
        mStartX = 100;
        mStartY = 300;
        mControlX = 200;
        mControlY = 100;
        mEndX = 300;
        mEndY = 300;
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        // 重置路径
        mPath.reset();
        
        // 移动到起点
        mPath.moveTo(mStartX, mStartY);
        
        // 绘制二次贝塞尔曲线
        mPath.quadTo(mControlX, mControlY, mEndX, mEndY);
        
        // 绘制曲线
        canvas.drawPath(mPath, mPaint);
        
        // 绘制控制点和辅助线
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(2);
        canvas.drawLine(mStartX, mStartY, mControlX, mControlY, mPaint);
        canvas.drawLine(mControlX, mControlY, mEndX, mEndY, mPaint);
        
        // 绘制控制点
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(mStartX, mStartY, 5, mPaint);
        canvas.drawCircle(mControlX, mControlY, 5, mPaint);
        canvas.drawCircle(mEndX, mEndY, 5, mPaint);
    }
}

2.3、属性动画结合

public class BezierAnimationView extends View {
    private Path mPath;
    private Paint mPaint;
    private float mStartX, mStartY;
    private float mEndX, mEndY;
    private float mControlX, mControlY;
    private float mProgress = 0;
    
    public BezierAnimationView(Context context) {
        super(context);
        init();
        startAnimation();
    }
    
    private void init() {
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setStrokeWidth(5);
        mPaint.setStyle(Paint.Style.STROKE);
        
        mStartX = 100;
        mStartY = 300;
        mEndX = 500;
        mEndY = 300;
    }
    
    private void startAnimation() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setDuration(2000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setRepeatMode(ValueAnimator.REVERSE);
        animator.addUpdateListener(animation -> {
            mProgress = (float) animation.getAnimatedValue();
            // 更新控制点位置
            mControlX = mStartX + (mEndX - mStartX) * mProgress;
            mControlY = mStartY - 200 * mProgress;
            invalidate();
        });
        animator.start();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        mPath.reset();
        mPath.moveTo(mStartX, mStartY);
        mPath.quadTo(mControlX, mControlY, mEndX, mEndY);
        canvas.drawPath(mPath, mPaint);
    }
}

3、贝塞尔曲线的实际应用

3.1、平滑的动画效果

// 实现平滑的弹跳效果
public void createBounceAnimation(View view) {
    ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
    animator.setDuration(1000);
    animator.addUpdateListener(animation -> {
        float progress = (float) animation.getAnimatedValue();
        // 使用贝塞尔曲线计算位置
        float y = calculateBezierY(progress);
        view.setTranslationY(y);
    });
    animator.start();
}

private float calculateBezierY(float t) {
    // 二次贝塞尔曲线
    float startY = 0;
    float controlY = -200;
    float endY = 0;
    return (1 - t) * (1 - t) * startY + 2 * (1 - t) * t * controlY + t * t * endY;
}

3.2、自定义曲线运动路径

// 让 View 沿着贝塞尔曲线运动
public void moveAlongBezier(View view) {
    ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
    animator.setDuration(2000);
    animator.addUpdateListener(animation -> {
        float t = (float) animation.getAnimatedValue();
        // 计算曲线上的点
        PointF point = calculateBezierPoint(t);
        view.setX(point.x);
        view.setY(point.y);
    });
    animator.start();
}

private PointF calculateBezierPoint(float t) {
    // 三次贝塞尔曲线
    float startX = 100, startY = 100;
    float control1X = 200, control1Y = 0;
    float control2X = 300, control2Y = 200;
    float endX = 400, endY = 100;
    
    float x = (1 - t) * (1 - t) * (1 - t) * startX +
             3 * (1 - t) * (1 - t) * t * control1X +
             3 * (1 - t) * t * t * control2X +
             t * t * t * endX;
    
    float y = (1 - t) * (1 - t) * (1 - t) * startY +
             3 * (1 - t) * (1 - t) * t * control1Y +
             3 * (1 - t) * t * t * control2Y +
             t * t * t * endY;
    
    return new PointF(x, y);
}

3.3、高级应用

- 波浪效果:使用多条贝塞尔曲线实现波浪动画

- 平滑过渡:在页面切换时使用贝塞尔曲线实现平滑过渡

- 自定义控件:如贝塞尔曲线实现的进度条、滑块等

- 粒子效果:使用贝塞尔曲线控制粒子的运动轨迹

3.4、最佳实践

- 选择合适的贝塞尔曲线阶数:一般二次或三次曲线足够满足大多数需求

- 合理设置控制点:控制点的位置直接影响曲线的形状

- 结合属性动画:使用 ValueAnimator 控制参数 t 的变化

- 性能优化:在复杂动画中,考虑使用硬件加速和缓存

- 测试不同设备:确保在不同屏幕尺寸和密度下的效果一致

贝塞尔曲线是 Android 动效中非常重要的工具,通过合理使用贝塞尔曲线,可以创建出平滑、自然的动画效果。掌握贝塞尔曲线的基本原理和实现方法,对于 Android 开发者来说,是提升应用用户体验的重要技能之一。

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w