Android Studio Canvas 实现鼠标贝塞尔曲线拖尾特效
特效预览图
什么是贝塞尔曲线?
百度百科:
贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。
贝塞尔曲线于1962,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau演算法开发,以稳定数值的方法求出贝兹曲线。
贝塞尔曲线是计算机图形图像造型的基本工具,是图形造型运用得最多的基本线条之一。它通过控制曲线上的四个点(起始点、终止点以及两个相互分离的中间点)来创造、编辑图形。其中起重要作用的是位于曲线中央的控制线。这条线是虚拟的,中间与贝塞尔曲线交叉,两端是控制端点。移动两端的端点时贝塞尔曲线改变曲线的曲率(弯曲的程度);移动中间点(也就是移动虚拟的控制线)时,贝塞尔曲线在起始点和终止点锁定的情况下做均匀移动。注意,贝塞尔曲线上的所有控制点、节点均可编辑。这种“智能化”的矢量线条为艺术家提供了一种理想的图形编辑与创造的工具。本文即用贝塞尔曲线实现鼠标拖尾特效。
理解贝塞尔曲线
贝塞尔曲线数学理解、推导方法:怎么理解贝塞尔曲线? - 知乎
本文bezier求点公式参考论文:Finding a Point on a Bézier Curve: De Casteljau’s Algorithm
引用 贝塞尔曲线简单介绍_xiaozhangcsdn的博客-CSDN博客_bezier曲线 对贝塞尔曲线理解关键点进行简要介绍:
对于贝塞尔曲线,最重要的点是数据点和控制点。
数据点: 指一条路径的起始点和终止点。
控制点:控制点决定了一条路径的弯曲轨迹
根据控制点的个数,贝塞尔曲线被分为一阶贝塞尔曲线(0个控制点)、二阶贝塞尔曲线(1个控制点)、三阶贝塞尔曲线(2个控制点)等等。
特点一:曲线通过始点和终点,并与特征多边形首末两边相切于始点和终点,中间点将曲线拉向自己。
特点二:平面离散点控制曲线的形状,改变一个离散点的坐标,曲线的形状将随之改变(点对曲线具有整体控制性)。
特点三:曲线落在特征多边形的凸包之内,它比特征多边形更趋于光滑。
贝塞尔曲线图示
引用自 贝塞尔曲线的数学原理_程序人生-CSDN博客_贝塞尔曲线原理
一阶贝塞尔曲线(线段):
意义:由 P0 至 P1 的连续点, 描述的一条线段
二阶贝塞尔曲线(抛物线):
原理:由 P0 至 P1 的连续点 Q0,描述一条线段。
由 P1 至 P2 的连续点 Q1,描述一条线段。
由 Q0 至 Q1 的连续点 B(t),描述一条二次贝塞尔曲线。
经验:P1-P0为曲线在P0处的切线。
三阶贝塞尔曲线:
通用公式:
高阶贝塞尔曲线:
4阶曲线:
5阶曲线:
好了,了解完以上贝塞尔曲线的知识,我们该如何用贝塞尔曲线在Android Studio上做特效呢?以本文所提鼠标拖尾特效为例:
第一步
创建新工程
直接创建空白 activity 即可
package com.example.drawimagery;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
第二步
因为 java 好像并未提供n阶贝塞尔函数的 api,所以我们就自己写一个
创建 Bezier 文件
Bezier 文件作为主函数体文件,保存了 beizer(计算n阶贝塞尔曲线上点的位置)、factorial(阶乘)、rainBow(彩虹色)三个函数。其中贝塞尔曲线公式是运用下图公式计算得出
公式引用自 贝塞尔曲线 WPF MVVM N阶实现 公式详解+源代码下载 - ARM830 - 博客园
- n=有效坐标点数量
- i=坐标点的下标
- P=坐标
- t=时间百分比,在0~1之间,覆盖了整条曲线
因为
可得
硬转换成 java 后其代码如下:
(因为本文项目使用 Queue 进行数据存储所以使用 LinkedList 结构)
package com.example.drawimagery;
import java.util.LinkedList;
public class Bezier {
public static float[] bezier(LinkedList<Float> theArrayX, LinkedList<Float> theArrayY, float t){
//贝塞尔公式调用
float x = 0;
float y = 0;
//控制点数组
int n = theArrayX.size() - 1;
int size = theArrayX.size();