欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

贝塞尔曲线

程序员文章站 2022-07-12 23:16:38
...

不懂数学公式、不懂数学公式、不懂数学公式,具体绘制全是靠自己悟出来的
逻辑,

1.根据本次绘制总时长与当前时长,获取占比
2.根据所给出的坐标点,递归计算得出当前的点坐标,并累计添加到一个集合中,同时根据顺序绘制曲线
  贝塞尔曲线

代码:


package com.project.git.com.gitproject.bezier;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

import com.project.git.com.gitproject.R;
import com.utilproject.wy.DeviceUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * created by wangyu on 2019-12-03
 * description : wy
 */
public class BezierView extends View {

    /**
     * 用于定位的点
     */
    private List<PointF> points = new ArrayList<>();
    private List<PointF> mFrameRects = new ArrayList<>();
    /**
     * 曲线上的点
     */
    private List<PointF> lines = new ArrayList<>();
    /**
     * 动画持续时间
     */
    private float duration = 5000;
    /**
     * 单次动画的开始时间
     */
    private long startTime = 0;

    /**
     * 曲线画笔
     */
    private Paint paint;

    /**
     * 边框、点等画笔
     */
    private Paint mRectPaint = null;

    /**
     * 与ys一起,用于计算当前最新的一个点
     */
    List<Float> xs = new ArrayList<>();
    List<Float> ys = new ArrayList<>();

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

    public BezierView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BezierView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(3);

        mRectPaint = new Paint();
        mRectPaint.setAntiAlias(true);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        points.clear();
        int maxHeight = h - 100;
        int height = maxHeight / 3;
        int wid = DeviceUtil.getScreenWidth(getContext()) / 2 - 50;
        for (int i = 0; i < 4; i++) {
            PointF point = new PointF();
//            point.x = w / 2 + (i % 3 == 0 ? 0 : (i > 1 ? wid : -wid));
//            point.y = h - 50 - i * height;
            point.x = i % 3 == 0 ? 50 : w - 50;
            point.y = i < 2 ? 50 : i == 3 ? h / 2 - 50 : h - 50;
            points.add(point);
        }
        for (int i = 0; i < 4; i++) {
            PointF point = new PointF();
            point.x = i % 3 == 0 ? 50 : w - 50;
            point.y = i < 2 ? 50 : h - 50;
            mFrameRects.add(point);
        }
    }

    public void startDraw() {
        BezierView.this.postDelayed(new Runnable() {
            @Override
            public void run() {
                startTime = System.currentTimeMillis();
                lines.clear();
                lines.add(points.get(0));
                invalidate();
            }
        }, 50);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mRectPaint.setColor(getResources().getColor(R.color.systemColor));
        mRectPaint.setStrokeWidth(2);
        for (int i = 0; i < mFrameRects.size(); i++) {
            //画边框
            int index = (i + 1) % mFrameRects.size();
            canvas.drawLine(mFrameRects.get(i).x, mFrameRects.get(i).y, mFrameRects.get(index).x, mFrameRects.get(index).y, mRectPaint);
        }
        if (lines.isEmpty()) {
            return;
        }
        if (System.currentTimeMillis() - startTime > (duration - 50)) {
            //如果当前时间与本次动画的开始时间,超过单次动画时长,开始新一次动画
            lines.clear();
            startDraw();
            return;
        }
        float t = (System.currentTimeMillis() - startTime) % duration / (duration - 50);//计算当前位置
        xs.clear();
        ys.clear();
        while (xs.size() != 1 && ys.size() != 1) {
            //根据当前位置(时间),递归计算出当前位置坐标
            //计算方法,根据当前位置(时间)与单次动画的持续时间的比例,循环计算出points中连续两个点中间的坐标,
            // 然后再将本次结果存入xs,ys,作为类似points的集合,继续循环计算,并重新保存到xs,ys中,
            // 直到最终只有一个坐标点时,即当前位置(时间)的结果,作为最新的一个点,存入lines中
            if (xs.isEmpty()) {
                //为空时,根据points计算第一轮中间值
                for (int i = 0; i < points.size() - 1; i++) {
                    xs.add(points.get(i).x * (1 - t) + points.get(i + 1).x * t);
                    ys.add(points.get(i).y * (1 - t) + points.get(i + 1).y * t);
                }
                for (int i = 0; i < points.size(); i++) {
                    //最外层的线与点
                    int index = (i + 1) % points.size();
                    mRectPaint.setColor(Color.BLUE);
                    canvas.drawLine(points.get(i).x, points.get(i).y, points.get(index).x, points.get(index).y, mRectPaint);
                    float x = points.get(i).x + (points.get(index).x - points.get(i).x) * t;
                    float y = points.get(i).y + (points.get(index).y - points.get(i).y) * t;
                    mRectPaint.setColor(Color.RED);
                    canvas.drawCircle(x, y, 7, mRectPaint);
                }
            } else {
                //非空时,新建集合缓存并循环计算新一轮的中间值
                List<Float> nXs = new ArrayList<>();
                List<Float> nYs = new ArrayList<>();
                nXs.addAll(xs);
                nYs.addAll(ys);
                for (int i = 0; i < nXs.size() - 1 && nXs.size() > 1; i++) {
                    //里层的线与点
                    int index = (i + 1) % nXs.size();
                    mRectPaint.setColor(0xff62C655);
                    canvas.drawLine(nXs.get(i), nYs.get(i), nXs.get(index), nYs.get(index), mRectPaint);
                    float x = nXs.get(i) + (nXs.get(index) - nXs.get(i)) * t;
                    float y = nYs.get(i) + (nYs.get(index) - nYs.get(i)) * t;
                    mRectPaint.setColor(Color.RED);
                    canvas.drawCircle(x, y, 7, mRectPaint);
                }
                xs.clear();
                ys.clear();
                for (int i = 0; i < nXs.size() - 1; i++) {
                    xs.add(nXs.get(i) + (nXs.get(i + 1) - nXs.get(i)) * t);
                    ys.add(nYs.get(i) + (nYs.get(i + 1) - nYs.get(i)) * t);
                }
                nXs.clear();
                nYs.clear();
            }
        }
        PointF point = new PointF();
        point.x = xs.get(0);
        point.y = ys.get(0);
        lines.add(point);
        if (lines.size() > 1) {
            paint.setStrokeWidth(4);
            for (int i = 0; i < lines.size() - 1; i++) {
                //曲线
                canvas.drawLine(lines.get(i).x, lines.get(i).y, lines.get(i + 1).x, lines.get(i + 1).y, paint);
            }
        }
        mRectPaint.setColor(Color.BLACK);
        canvas.drawCircle(point.x, point.y, 7, mRectPaint);
        invalidate();
    }

    /*
     * Drawable → Bitmap
     */
    private static Bitmap drawable2Bitmap(Drawable drawable, float f) {
        if (drawable == null) {
            return null;
        }
        // 取 drawable 的长宽
        int w = Math.round(drawable.getIntrinsicWidth() * f);
        int h = Math.round(drawable.getIntrinsicHeight() * f);
        // 取 drawable 的颜色格式
        Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                : Bitmap.Config.RGB_565;
        // 建立对应 bitmap
        Bitmap bitmap = Bitmap.createBitmap(w, h, config);
        // 建立对应 bitmap 的画布
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        // 把 drawable 内容画到画布中
        drawable.draw(canvas);
        return bitmap;
    }
}