– | – |
---|---|
在玩贝塞尔之前先做点准备活动热热身。打个网格对学习贝塞尔曲线是很有帮助的。如下是以中心为原点的坐标系,
x向右
,y向下
# 0.1 : 主程序
1 | void main() => runApp(MyApp()); |
# 0.2 : 自定义 Paper 组件显示画布
为了绘制的纯粹和雅观,这里把状态量去掉,并且手机横向。
1 |
|
# 0.3 : 绘制网格
注意:这里永久的将画布原点移到画布的中心点,之后所以的绘制都将以中心为 (0,0) 点。
1 |
|
# 0.4、人生至美莫初见
先不看哪些花里胡哨的贝塞尔曲线的动画。让我们从实践中一点点去摸索。如此美丽的初见,为何要这么复杂?当你渐渐去认识她,了解她,熟悉她,便会明白:
哦,原来如此如此,这般这般...
- 看到
贝塞尔
三个字,也不用觉得压力太大,满打满算也就两个函数而已。
1 | ---->[二次贝塞尔曲线]---- |
# 一、二次贝塞尔曲线
二次贝塞尔曲线需要传入四个
double
类型的值。
# 1. 先画一笔看看
首先新准备个画笔和路径,在构造函数里初始化。准备两个测试点
p1,p2
,
然后轻轻的用quadraticBezierTo
描一笔,就出来一个曲线。
1 | class BezierPainter extends CustomPainter { |
# 2. 为什么曲线会是这样的?
为了更好的理解贝塞尔曲线,现在我们需要绘制辅助帮我们理解。现在想将与贝塞尔曲线有关系的三个点画出来。同样,我不想弄脏画笔,所以新拿一个
_helpPaint
。在_drawHelp
方法里进行绘制辅助线。
1 | class BezierPainter extends CustomPainter { |
- 看到上图,你是不是发现的什么?如果还比较懵,再画一道辅助线
1 | void _drawHelp(Canvas canvas) { |
# 3. 来玩一下这个曲线
这不就是三个点嘛,要能拖拖看就好了。没问题,应你所求
现在有两个要点: 【1】 如何获取触点 【2】如何通过一个触点控制三个点位
简单讲解
由于点位需要变化,BezierPainter 只承担绘制的责任
,这里在组件中定义 点位信息_pos
和 选中索引_selectIndex
,通过构造函数传入 BezierPainter。为了方便大家玩耍,我单独写个文件 play_bezier2.dart
里面有个 PlayBezier2Page 组件。
1 | ---->[_PaperState]---- |
获取触点信息
通过GestureDetector组件
可以获取触点信息,然后传给画布即可。
这里的思路很清晰:在点击时需要判断点击了哪个点,抬起时取消选中点,移动时变化选中点。
1 | @override |
一个触点控制三个点位
这就有点技术含量了。需要进行
点域的判断
来确定当前点击的是哪个点。
比如在半径为 6 的区域内算作命中
,就需要在点击时判断是否命中某个点。具体逻辑为:
1 | ///判断出是否在某点的半径为r圆范围内 |
前三个点需要用户点击,然后画出一段二贝曲线,之后再点击不会添加点,而是判断是否触点在期望的圆域内。这样数据的处理就完成了。根基【捷特第二定理】
一切的界面交互和动态视觉效果都是连续时间点状态量的变化和刷新的结合。
现在所有的状态量和刷新都已经实现,剩下的就是将这些量显示在界面上。
1 | @override |
绘制
网格和辅助的和上面逻辑基本一致,详见源码,这里就不贴了。当点数小于三个时,仅绘制触点,否则绘制曲线和辅助线。
有一点需要注意:我们的点位是相对于屏幕左上角的,需要平移到画布中心
1 | class BezierPainter extends CustomPainter { |
通过前面的介绍,一段二次的贝塞尔曲线有三个点决定,
起点
、控制点
、终点
关于起点,默认是(0,0),你也在绘制之前 moveTo 设置起点,当绘制连续的贝塞尔曲线,下一段曲线的起点就是上一段的终点。所以二次贝塞尔曲线至关重要的是两个点:也就是入参中的控制点和终点
。
# 二、三次贝塞尔曲线
前面的二次贝塞尔实现了,那现在来看三次的
cubicTo
。需要六个参数,也就是三个点。
我们可以使用之前的代码,很快捷的生成如下效果。源代码在play_bezier3.dart
# 1. 实现三贝单线操作
前面点集在
_pos
中维护,现在需要四个点,so easy
点击时将限制数改为4个
1 | ---->[_PlayBezier3PageState]---- |
绘制将限制数改为4个
1 | if(pos.length<4){ |
That is all
, 这就是分工明确的好处,变化时只变需变化待变化的,整体的流程和思路是恒定的。
# 2. 三贝中的拟圆
三贝很厉害,可以说无所不能。只有你想不到,没有她做不到
Ps 中的钢笔路径就是多段的三贝曲线。所以还是很有玩头的。
–
绘制拟圆
下面的图看着像个圆,但其实是四段三贝拟合而成的。目前我们的代码中最在意的就是点位数据。所以关键就是寻找点。本小节源码在:
circle_bezier.dart
中
第一段-左下
这里直接给出点,至于
0.551915024494
是什么,后面有机会会带你一起推导。有兴趣的话,你也可以自己查一查资料。和之前一样,核心的绘制就是那么一句。
1 | ---->[CircleBezierPage]---- |
其他三段
初始点时,将这 12 点放入列表。然后将赋值的点线绘制出来。
1 | ---->[CircleBezierPage#_initPoints]---- |
# 3. 三贝中的拟圆的操作
看这控制柄,满满的拖动欲望,来实现一下吧
有了之前的铺垫,下面的代码应该很容易接受吧。
1 | @override |
# 三、贝塞尔曲线与路径操作
也许你觉得贝塞尔曲线也就那样。那么你忽略了一个很重要的东西。
贝塞尔曲线
是一条路径。路径是个什么东西,之前写了一篇关于路径使用的冰山一角
【Flutter 高级玩法 - shape】Path 在手,天下我有
现在再准备一条路径,看看路径间的如何操作
1 | class BezierPainter extends CustomPainter { |
# 1. 路径的相减: PathOperation.difference
1 | @override |
# 2. 路径的相加: PathOperation.union
1 | @override |
# 3. 路径的反减: PathOperation.reverseDifference
1 | @override |
# 4. 路径的交集: PathOperation.intersect
1 | @override |
# 5. 路径的反交集: PathOperation.xor
当然路径并非是线条,也可以进行填色。
1 | @override |
OK, 本篇到这里就告一段落,下一篇会找几个实际的用途,来看看贝塞尔曲线的妙用。 敬请期待。