Flutter - 利用 ClipPath 实现任意形状 Widget

:lollipop: 关于 ClipPath

我们应该都使用过 ClipXXX 相关的组件, 来实现一些  圆角矩形/圆形形状 十分的方便,那如果想要实现一些奇形怪状的 Widget,例如  五角星/圆弧形 之类的,那就只能用  ClipPath 了。

想要了解 ClipPath ,还是直接去官网撸文档,介绍如下:

A widget that clips its child using a path.

Calls a callback on a delegate whenever the widget is to be painted. The callback returns a path and the widget prevents the child from painting outside the path.

Clipping to a path is expensive.

用 path 来剪切 child 的 widget。

每当要绘制小部件时,都会在委托上调用回调。回调函数返回一个路径,并且该 widget 可防止 child 在 path 外绘制。

裁剪 path 很昂贵。

总的来说,也就是按照路径来剪切子 widget,但是 裁剪 path 很昂贵

来看一下怎么使用

关于如何使用,我们还是先来看一下他的构造函数:

const ClipPath({

Key key,

this.clipper, // final CustomClipper<Path> clipper;

this.clipBehavior = Clip.antiAlias,

Widget child,

}) : assert(clipBehavior != null),

super(key: key, child: child);

首先可以看到需要的参数其实就两个,一个是 clipper ,另一个是  child

child 就是被  clipper 裁剪的组件,具体是啥自己来写就行了,剩下的就是  clipper

看一下 clipper 的源码:

/// CustomClipper

abstract class CustomClipper<T> {

/// Creates a custom clipper.

///

/// The clipper will update its clip whenever [reclip] notifies its listeners.

const CustomClipper({ Listenable reclip }) : _reclip = reclip;


final Listenable _reclip;


/// 返回 clip 的说明 -> T

T getClip(Size size);


/// 是否重新 clip

bool shouldReclip(covariant CustomClipper<T> oldClipper);


}

这里去掉了一些方法,只保留了我们需要重写的,其中最主要的就是 T getClip(Size size)

ClipPath 里传入的泛型为  <Path> ,其实我们熟知的  ClipRectClipRRectClipOval 也就是对应着  CustomClipper<Rect>CustomClipper<RRect>CustomClipper<Rect> 而已。

所以在这里我们只需要定义好自己的 Path 就可以实现任意形状的 Widget 了。

:smirk_cat: 开始实现自定义形状的 Widget

我们来实现如下形状(上面是原图、下面是裁剪过的):

综上所述,只需要实现一个 CustomClipper<Path> 然后传入 ClipPath 的  clipper 参数即可。

代码如下:

class MyClipper extends CustomClipper<Path> {


@override

Path getClip(Size size) {

Path path = Path();

// 从 60,0 开始

path.moveTo(60, 0);

// 二阶贝塞尔曲线画弧

path.quadraticBezierTo(0, 0, 0, 60);

// 连接到底部

path.lineTo(0, size.height / 1.2);

// 三阶贝塞尔曲线画弧

path.cubicTo(size.width / 4, size.height, size.width / 4 * 3, size.height / 1.5, size.width, size.height / 1.2);

// 再连接回去

path.lineTo(size.width, 60);

// 再用二阶贝塞尔曲线画弧

path.quadraticBezierTo(size.width - 60, 60, size.width - 60, 0);

return path;

}


@override

bool shouldReclip(CustomClipper<Path> oldClipper) => false;

}

逻辑就不说啦,都在注释里。

总结

因为 ClipPath 的消耗比较大,所以如果只是想裁剪个圆角之类的,还是推荐使用自带的  ClipRRect 之类的,他们的性能更好(官方文档所说)。

ClipPath 还有一个静态方法  ClipPath.shape() ,这个具体就不说了,有感兴趣的可以去翻源码查看。

也可以看看 张风捷特烈的这篇文章 - 【Flutter高级玩法-shape】Path在手,天下我有。 [1]

这篇文章详细的讲解了 Path 的玩法,只有你想不到,没有它做不到!在最后也有讲解该静态方法。

代码已经提交到了 Github - 裁剪 Widget Demo。 [2]

如有缺陷,希望大家提出,共同学习!

References

[1] 张风捷特烈的这篇文章 - 【Flutter高级玩法-shape】Path在手,天下我有。:  https://juejin.im/post/5e6196066fb9a07c8b5bbdf5

[2] Github - 裁剪 Widget Demo。:  https://github.com/wanglu1209/WFlutterDemo/blob/master/wx_demo_project/lib/clip_image_page.dart

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章