? 關於 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>
,其實我們熟知的 ClipRect
/ ClipRRect
/ ClipOval
也就是對應著 CustomClipper<Rect>
/ CustomClipper<RRect>
/ CustomClipper<Rect>
而已。
所以在這裡我們只需要定義好自己的 Path
就可以實現任意形狀的 Widget 了。
? 開始實現自定義形狀的 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在手,天下我有。
這篇文章詳細的講解了 Path
的玩法,只有你想不到,沒有它做不到!在最後也有講解該靜態方法。
程式碼已經提交到了 Github - 裁剪 Widget Demo。
如有缺陷,希望大家提出,共同學習!?