贝塞尔曲线——水波纹实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sliverbullets/article/details/80358544

贝塞尔曲线——水波纹实现

1.贝塞尔曲线来源

在数学的数值分析领域中,贝赛尔曲线(Bézier曲线)是电脑图形学中相当重要的参数曲线。更高维度的广泛化贝塞尔曲线就称作贝塞尔曲面,其中贝塞尔三角是一种特殊的实例。
贝塞尔曲线于1962年,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau算法开发,以稳定数值的方法求出贝塞尔曲线。

2.贝塞尔曲线公式

一阶贝塞尔曲线

B(t) = (1-t)Po + tP1 , t∈[0,1]
这里写图片描述
t的取值范围:是从p0到p1直线到达所用的时间,整体化最大为1.
其中,p0为起点,p1为终点,t表示当前时间,B(t)表示公示的结果。其函数意义为随时间t所形成的轨迹,一阶为直线匀速运动。

二阶贝塞尔曲线

B(t) = (1-t)² Po + 2t(1-t)P1 + t² P2, t ∈ [0,1]
这里写图片描述
其中,p0为起始点,p1是控制点,p2是中点。
p1控制点:有了p1,一阶的直线就变成曲线了!

三阶贝塞尔曲线

B(t) = Po(1-t)³ + 3P1 t(1-t)² + 3P2 t²(1-t) + P3 t³, t∈[0,1]
这里写图片描述
其中,P0是起始点,P3是终点,p1是第一个控制点,p2是第二个控制点。
分析:
整体由三条一阶贝塞尔曲线组成:
路线 随时间变化的点
P0---P1 Q0
P1---P2 Q1
P2---P3 Q2

然后又它们移动的点Q0、Q1、Q2有组成了2条一阶的贝塞尔曲线:
路线 随时间变化的点
Q0---Q1 R0
Q1---Q2 R1

最后由他们移动的点R0、R1构成了1条一阶贝塞尔曲线:
路线 随时间变化的点
R0 --- R1 B

就形成了最终的三阶贝塞尔曲线。
还有四阶、五阶……

3.贝塞尔曲线—quadTo

Path类里这四个方法与贝塞尔曲线有关

//二阶贝赛尔
public void quadTo(float x1, float y1, float x2, float y2)
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)
//三阶贝赛尔
public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
public void rCubicTo(float x1, float y1, float x2, float y2,float x3, float y3)

其中(x1,y1)是控制点坐标,(x2,y2)是终点坐标。
那么起点:(x2-x1,y2)

完整代码

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/reset"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="reset" />
<com.example.bsrdemo.MyView
android:id="@+id/myview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

自定义一个myView

package com.example.bsrdemo;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;

public class MyView extends View {
    private Paint mPaint;
    private Path mPath = new Path();
    private float mPreX,mPreY;
    private int mItemWaveLength = 1000;
    private int dx;

    public MyView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mPath = new Path();
    mPaint = new Paint();
    mPaint.setColor(Color.GREEN);
    mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
}

public MyView(Context context) {
    super(context);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:
            mPath.moveTo(event.getX() , event.getY());
            mPreX = event.getX();
            mPreY = event.getY();
            return true;
        case MotionEvent.ACTION_MOVE:
            float endX = (mPreX+event.getX())/2;
            float endY = (mPreY+event.getY())/2;
            mPath.quadTo(mPreX,mPreY,endX,endY);
            mPreX = event.getX();
            mPreY =event.getY();
            invalidate();
            break;
    }
    return super.onTouchEvent(event);
}


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

    mPath.reset();
    int originY = 300;
    int halfWaveLen = mItemWaveLength/2;
    mPath.moveTo(-mItemWaveLength +dx,originY);
    for (int i = -mItemWaveLength;i<=getWidth()+mItemWaveLength;i+=mItemWaveLength){
        mPath.rQuadTo(halfWaveLen/2,-100,halfWaveLen,0);
        mPath.rQuadTo(halfWaveLen/2,100,halfWaveLen,0);
    }
    mPath.lineTo(getWidth(),getHeight());
    mPath.lineTo(0,getHeight());
    mPath.close();
    canvas.drawPath(mPath,mPaint);
}

public void reset(){
    mPath.reset();
    invalidate();
}


public void startAnim(){
    final ValueAnimator animator = ValueAnimator.ofInt(0,mItemWaveLength);
    animator.setDuration(2000);
    animator.setRepeatCount(ValueAnimator.INFINITE);
    animator.setInterpolator(new LinearInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            dx = (int)animation.getAnimatedValue();
            postInvalidate();

        }
    });
    animator.start();
}

MainActivity.java
ackage com.example.bsrdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final MyView myView = (MyView)findViewById(R.id.myview);

    myView.startAnim();

    Button reset_btn = findViewById(R.id.reset);
    reset_btn.setOnClickListener(new View.OnClickListener( ) {
        @Override
        public void onClick(View v) {
            myView.reset();
        }
    });
}
}

猜你喜欢

转载自blog.csdn.net/sliverbullets/article/details/80358544