Flutter - 利用 ClipPath 實現任意形狀 Widget

🍭 關於 ClipPath

咱們應該都使用過 ClipXXX 相關的組件, 來實現一些 圓角矩形/圓形形狀十分的方便,那若是想要實現一些奇形怪狀的 Widget,例如 五角星/圓弧形之類的,那就只能用 ClipPath了。git

想要了解 ClipPath,仍是直接去官網擼文檔,介紹以下:github

A widget that clips its child using a path.markdown

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.ide

Clipping to a path is expensive.函數

用 path 來剪切 child 的 widget。post

每當要繪製小部件時,都會在委託上調用回調。回調函數返回一個路徑,而且該 widget 可防止 child 在 path 外繪製。性能

裁剪 path 很昂貴。學習

總的來講,也就是按照路徑來剪切子 widget,可是裁剪 path 很昂貴ui

🦾 來看一下怎麼使用

關於如何使用,咱們仍是先來看一下他的構造函數:this

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>,其實咱們熟知的 ClipRect / ClipRRect / ClipOval 也就是對應着 CustomClipper<Rect> / CustomClipper<RRect> / CustomClipper<Rect> 而已。

因此在這裏咱們只須要定義好本身的 Path 就能夠實現任意形狀的 Widget 了。

😼 開始實現自定義形狀的 Widget

咱們來實現以下形狀(上面是原圖、下面是裁剪過的):

綜上所述,只須要實現一個 CustomClipper<Path> 而後傳入ClipPathclipper 參數便可。

代碼以下:

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在手,天下我有。

這篇文章詳細的講解了 Path 的玩法,只有你想不到,沒有它作不到!在最後也有講解該靜態方法。

代碼已經提交到了 Github - 裁剪 Widget Demo。

若有缺陷,但願你們提出,共同窗習!🤝

相關文章
相關標籤/搜索