Flutter绘制进阶——贝塞尔曲线

前言

说到贝塞尔曲线各位前端的小伙伴一定不陌生,贝塞尔曲线是一段优美的曲线,他可以
极大程度的提高我们程序的美观性,试想我们的应用如果只有简单的矩形、圆形、三角形巴
拉巴拉的基础图形,很难达到我们期望的良好的交互效果。我们如何去随性所欲的裁切、遮
盖、绘制我们想要的界面的,今天的主角——贝塞尔曲线,必不可少。

贝塞尔曲线

开门见山,要学会贝塞尔曲线,肯定必须要了解,什么是贝塞尔曲线啊。不知道有没有小伙伴和我一样,一开始有被这个名字吓到,第一感觉就是:高端,复杂,难搞。所以别慌,接下来它不叫贝塞尔了,我就叫他:简单曲线!
  OK,不搞大家心态了,其实没有难学的技术,只怕肯钻研的开发。给大家揭开它的神秘面纱了:

贝塞尔曲线(Bezier curve)是计算机图形学中相当重要的参数曲线,它通过一个方程来描述一条曲线,根据方程的最高阶数,又分为线性贝赛尔曲线,二次贝塞尔曲线、三次贝塞尔曲线和更高阶的贝塞尔曲线。

如上所述,贝塞尔曲线有很多次,二次、三次、四次…,今天主要给各位介绍的是二次的贝塞尔曲线。

二次贝塞尔曲线

二次贝塞尔曲线由三个点P0,P1,P2来确定,这些点也被称作控制点。曲线的方程为:
  在这里插入图片描述

啊?我人裂开了,这人都看傻了呀,写的啥呀?别急,一点一点的来剥离这个公式。首先根据第一句话,我们可以明确的是,公式中的P0,P1,P2分别代表的是三个点。这个t呢?这个t各位可以暂时看作一个从0到1之间的一个数。这样整个公式中的各个字符所代表的含义也就明确了。
看图细说(必看):

Flutter绘制进阶——贝塞尔曲线_第1张图片
选定一个0~1的t值
通过P0P1计算出点Q0Q0P0 P1连成的直线上,并且length( P0, Q0 ) = length( P0, P1 ) * t
同样,通过P1P2计算出Q1,使得length( P1, Q1 ) = length( P1, P2 ) * t
再重复一次这个步骤,通过Q1Q2计算出B,使得length( Q0, Q1 ) = length( Q0, B ) * tB就为当前曲线上的点

注:上面的length表示两点之间的长度。
  哎,认真阅读完上面一段,并且理解的同学,应该已经发现,我们通过上面的手段,得到了一个点——点B。那么,如何由点到线的过渡呢?追本溯源,各位看一下我们最开始的公式,我们一开始就假设了t是一个常量,但其实不然。这个t就是我们画出这条曲线的方法。如果将t的值从0过渡到1,不断计算点B,就可以得到一条二次贝塞尔曲线:

在这里插入图片描述

Flutter中的贝塞尔

其实在canvas中已经有绘制二次贝塞尔曲线,在Flutter中就是这个方法:

  /// Adds a quadratic bezier segment that curves from the current
  /// point to the given point (x2,y2), using the control point
  /// (x1,y1).
  void quadraticBezierTo(double x1, double y1, double x2, double y2) native 'Path_quadraticBezierTo';

这个方法中我们传入了看起来是四个坐标?好像很复杂的样子?一点都不复杂,咱们在上文中已经解开了疑惑。其中x1, y1, x2, y2即为后两个控制点(P1P2)的横纵坐标。那P0勒?P0就是你当前所在的位置。说的还是很抽象,来实战吧~
起飞 ️
  打开DartPad,新建一个Flutter Pad,我们的第一步就要新建一个Container,放入我们的主题文字,给上一个背景色,没啥技术含量,我一步带过了~,希望各位跟上。
Flutter绘制进阶——贝塞尔曲线_第2张图片
 然后我们要在我们的Container外层嵌套一个裁切组件,用于裁切我们这个组件。我们用的组件就是ClipPath,顾名思义,我们这个组件既然是裁切所用,各位想想,一个裁衣工,在裁剪衣服时依据是什么?是裁衣服的路线呀~,所以我们这个组件中的必要参数:clipper就是一个自定义的路线罢了。
Flutter绘制进阶——贝塞尔曲线_第3张图片
 我们嵌套完成以后,来定义我们的裁切路线咯,首先看到的是两个必须实现的父类方法,一个返回Path的方法不用说,肯定是我们的路线,另一个方法shouldReclip是让我们决定是否需要重新裁切✂️,这不是我们的重点,直接给个true完事儿。
Flutter绘制进阶——贝塞尔曲线_第4张图片
万事俱备,正式开工咯!我们来新建一个path,我们的起点是坐标原点,也就是左上角,我们认为是(0,0),然后我们首先到了哪个点呢?

path.lineTo(0, size.height - 80);

这是哪个点?是我们从原点直接向下走,走到离我们的Container高度还剩80处,我们要开始画贝塞尔曲线了!上面就说过,我们要画贝塞尔曲线,我们要传入两个点的坐标!是后面两个点!P1P2,自己定义两个呗。定义好以后我们调用path自带绘制贝塞尔方法试试。

    /// P1
    var controllPoint = Offset(50, size.height);
    /// P2
    var endPoint = Offset(size.width/2, size.height);
    /// 绘制贝塞尔曲线
    path.quadraticBezierTo(controllPoint.dx, controllPoint.dy, endPoint.dx, endPoint.dy);

有人会问,P0跑哪儿去了,其实不难发现,P0就是我们最开始走到的那个距离Container下边界还有80的点,也就是在贝塞尔中的起点~,来看一下效果图吧~我已经帮各位把点都标注出来了。


  看见这美丽的弧度了吗?虽然很普通,但确实是我们的贝塞尔的起源啊,有一个弧度就有一万种弧度!但是好像…我们切的有点多,为啥捏,因为我们的终点就到了P2,起点是左上角,这连起来确实是这样~我们只需要把我们未走完的路走完就行了~

Flutter绘制进阶——贝塞尔曲线_第5张图片

实现代码:
import 'package:flutter/material.dart';
​
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
​
void main() {
  runApp(MyApp());
}
​
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Column(children: [
          ClipPath(
            clipper: MyClipper(),
            child: Container(
              height: 320,
              decoration: BoxDecoration(color: Colors.redAccent),
              child: Center(
                child: Text(
                  "bezier",
                  style: TextStyle(
                      color: Colors.cyanAccent,
                      fontSize: 45,
                      fontWeight: FontWeight.w700),
                ),
              ),
            ),
          )
        ]),
      ),
    );
  }
}
​
class MyClipper extends CustomClipper
{
  @override
  Path getClip(Size size) {
    var path = new Path();
    path.lineTo(0, size.height - 80);
    var controllPoint = Offset(50, size.height);
    var endPoint = Offset(size.width/2, size.height);
    path.quadraticBezierTo(controllPoint.dx, controllPoint.dy, endPoint.dx, endPoint.dy);
    path.lineTo(size.width, size.height);
    path.lineTo(size.width, 0);
    return path;
  }
​
  @override
  bool shouldReclip(CustomClipper oldClipper) {
    return true;
  }
}
​

感谢观看,欢迎点赞,评论!!!

你可能感兴趣的:(Flutter)