Flutter中的贝塞尔曲线切割

968 阅读1分钟

一般公司美工的设计图中增加了许多曲线设计,比如个人页面,还有首页的一些页面,之前比较偷懒的方式是让美工同学做一个PNG的透明图片,然后外边放一个Container.但其内容如果本身就不是图片,只是容器,这种放入图片的做法会让包体变大。其实我们完全可以使用贝塞尔曲线进行切割。页面如下图这种:

实现方式是通过ClipPath路径裁切控件,ClipPath主要有两个属性:

  • child :要切割的元素,可以是容器,图片等等;
  • clipper : 切割的路径,这个要和CustomClipper对象配合使用;

CustomClipper裁切路径

ClipPath中指定clipper ,自定义继承 CustomClipper,重写getClip方法 ,返回裁切路径。

一个二阶的贝塞尔曲线是需要控制点和终点的,控制点就像一块磁铁,把直线吸引过去,形成一个完美的弧度,这个弧度就是贝塞尔曲线了。

class MyClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();
    path.lineTo(0, size.height - 80);//设置起点
    path.quadraticBezierTo(
        size.width / 2, //第二个点x坐标
        size.height, //第二个点y坐标
        size.width, //第三个点x坐标
        size.height - 80);//第四个点y坐标
    path.lineTo(size.width, 0);//设置终点
    path.close();
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    return false;
  }
}

class _MyHeaderState extends State<MyHeader> {
  @override
  Widget build(BuildContext context) {
    return ClipPath(
      clipper: MyClipper(),
      child: Container(
        padding: EdgeInsets.only(left: 40, top: 50, right: 20),
        height: 350,
        width: double.infinity,
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topRight,
            end: Alignment.bottomLeft,
            colors: [
              Color(0xFF3383CD),
              Color(0xFF11249F),
            ],
          ),
          image: DecorationImage(
            image: AssetImage("assets/images/virus.png"),
          ),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          children: <Widget>[
            GestureDetector(
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) {
                      return InfoScreen();
                    },
                  ),
                );
              },
              child: SvgPicture.asset("assets/icons/menu.svg"),
            ),
            SizedBox(height: 20),
            Expanded(
              child: Stack(
                children: <Widget>[
                  Positioned(
                    top: (widget.offset < 0) ? 0 : widget.offset,
                    child: SvgPicture.asset(
                      widget.image,
                      width: 230,
                      fit: BoxFit.fitWidth,
                      alignment: Alignment.topCenter,
                    ),
                  ),
                  Positioned(
                    top: 20 - widget.offset / 2,
                    left: 150,
                    child: Text(
                      "${widget.textTop} \n${widget.textBottom}",
                      style: kHeadingTextStyle.copyWith(
                        color: Colors.white,
                      ),
                    ),
                  ),
                  Container(), // I dont know why it can't work without container
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

上面代码的基础上修改为波浪式的贝塞尔曲线,波浪形式的只要把裁切变成两个对称的贝塞尔曲线就可以实现了。

class MyClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();
    path.lineTo(0, size.height - 80);
    path.quadraticBezierTo(
      size.width / 4, size.height,
      size.width / 2, size.height -80,);
    path.quadraticBezierTo(
        size.width / 4 * 3, size.height - 160,
        size.width, size.height - 80);
    path.lineTo(size.width, 0);
    path.close();
    return path;
  }