貝塞爾曲線

不懂數學公式、不懂數學公式、不懂數學公式,具體繪製全是靠自己悟出來的
邏輯,

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;
    }
}
發佈了38 篇原創文章 · 獲贊 7 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章