原文作者:medium.com/@rahiche
釋出時間:2018年10月9日 - 閱讀6分鐘
在計算機圖形學中,將渲染限制在某一特定區域的行為被稱為 "剪下"。剪輯區域被提供給畫布,這樣渲染引擎將只 "繪製 "定義區域內的畫素。在該區域外 "繪製 "的任何東西都不會被渲染。
作為開發者,我們使用剪接來建立具有特殊效果的、看起來很棒的、自定義的使用者介面。正如標題所說,這裡我們將討論如何在Flutter中進行剪下,讓你在短時間內建立令人瞠目結舌的?介面。
入門
讓我們從建立一個赤裸裸的應用程式開始,在Scaffold的中心建立一個簡單的200x200的紅色矩形。
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
home: MyApp(),
));
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Container(
color: Colors.red,
width: 200.0,
height: 200.0,
),
),
);
}
}
複製程式碼
定製剪下
CustomClipper
是Flutter中剪裁的基礎類,它被四個小部件使用。ClipRect
, ClipRRect
, ClipOval
, ClipPath
。
每一個都有一個定義的行為,所以如果你在ClipOval
中包裹一個紅色Container
,結果是一個圓形。
改變Container
的高度或寬度,它就會變成一個橢圓形。想想看,在Flutter中調整這樣的橢圓是多麼的快速和容易;您所要做的就是改變高度和/或寬度,並按Ctrl+\
為熱重新載入,看看您的變化在不到一秒鐘的時間裡是如何的。大一點,寬一點,不也許小一點......
現在,如果你想自定義剪輯的大小和位置,這就是CustomClipper
的作用。讓我們為我們的ClipOval
Widget建立一個吧
class CustomRect extends CustomClipper<Rect>{
@override
Rect getClip(Size size) {
// TODO: implement getClip
}
@override
bool shouldReclip(CustomRect oldClipper) {
// TODO: implement shouldReclip
}
}
複製程式碼
注意:在類的繼承部分,對通用型別進行具體說明是非常重要的,你可以從 clipper 屬性中看到你的 widget 需要什麼。在這個例子中是Rect
。
當你從CustomClipper
類繼承時,你需要覆蓋兩個方法。
第一個是getClip()。這為你提供了被剪下的RenderBox的大小,並要求你返回一個Rect
。這個Rect
將定義剪輯的大小和位置。順便說一下:每個可見的Widget
都有一個RenderBox
)。
所以,如果我們返回這個:
Rect getClip(Size size) {
Rect rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
return rect;
}
複製程式碼
程式碼說明:使用fromLTRB建構函式,我們必須提供左、上、右、下的點。
這不會有任何改變,因為這裡的 Rect 和 widget 的邊界框是一樣的。
但是如果你想只顯示橢圓的一半呢?我們可以很容易做到,將矩形的左點改為-size.width
,如果想看另一半,只需將右點改為size.width*2
即可。
你需要覆蓋的另一個方法是shouldReclip()。每當你提供一個新的物件(在本例中是CustomClipper<Rect>
)被建立時,這個方法就會被呼叫,以與舊的物件進行比較,當它返回true時,getClip就會被呼叫,而不是反過來,因為當盒子的大小改變時,getClip方法也會被觸發。
快速注意:測試時要確保shouldReclip返回true。如果沒有,請明確地設定它,這樣Hot Reload就會正確地改變剪輯區域。
各種剪下Widget
到目前為止,我們只介紹了一般的剪下,並舉了使用ClipOval
的例子。現在讓我們來談談其他的剪裁部件,看看每個部件提供了什麼。
ClipRect
用來從一個較大的影像中剪出一個矩形區域。例如,如果你只想要較大影像中的一小塊矩形區域,你可以使用ClipRect。
Container(
child: Align(
alignment: Alignment.bottomRight,
heightFactor: 0.5,
widthFactor: 0.5,
child: Image.network(
"https://static.vinepair.com/wp-content/uploads/2017/03/darts-int.jpg"),
),
),
複製程式碼
ClipRRect
這和ClipRect
一樣,都是圓角。請注意,你可以為每個角設定不同的曲率,而不是強迫你讓四個角的半徑都一樣。在這個例子中,只有三個角被賦予了數值。我們要做的就是讓左下角保持 "正方形",並且不給它分配任何值。預設情況下它是正方形的。
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25.0),
topRight: Radius.circular(25.0),
bottomRight: Radius.circular(25.0),
),
child: Align(
alignment: Alignment.bottomRight,
heightFactor: 0.5,
widthFactor: 0.5,
child: Image.network(
"https://static.vinepair.com/wp-content/uploads/2017/03/darts-int.jpg"),
),
)
複製程式碼
ClipPath
用來使用路徑夾住一個自定義區域。如果你能用ClipRect
、ClipRRect
或ClipOval
實現你想要的東西,那麼你真的想用它們來代替。但是當你需要的時候,ClipPath
可以成為你的救星。
比方說,你想剪輯一個三角形。
class TriangleClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final path = Path();
path.lineTo(size.width, 0.0);
path.lineTo(size.width / 2, size.height);
path.close();
return path;
}
@override
bool shouldReclip(TriangleClipper oldClipper) => false;
}
複製程式碼
clipBehavior
clipBehavior
屬性是新的,你會在Material Widgets中看到很多,因為幾個月前討論的突破性變化。有了這個變化,Flutter應用程式的速度快了30%,但這個突破性的變化的一部分是它禁用了對saveLayer
的自動呼叫,它暴露了一個clipBehavior
屬性。有了它,你可以設定如何剪輯widget內容。大多數小元件的預設行為是Clip.none
。
Clip.hardEdge
Clip.antiAlias
Clip.antiAliasWithSaveLayer.
正如你所看到的,antiAliasWithSaveLayer和antiAlias之間沒有明顯的區別,但是和hardEdge有明顯的區別。在效能方面,它們之間有很大的區別,最快的是hardEdge,而antiAliasWithSaveLayer是最慢的。
渲染
在構建圖層樹的過程中,所有的剪下部件都會在PaintingContext中應用它們的剪下區域。當GPU將結果內容繪製到幀緩衝區時,我們新增的每一個圖層都會增加其複雜性。我們總是建議儘量減少剪裁,因為這會給GPU增加每個圖層的模板緩衝區,這樣會增加很多渲染時間。最好將剪裁控制在需要的範圍內,我們需要注意在動畫製作過程中儘量少用。如果你想知道的話,那麼請看Adam Barth在2016年介紹的Flutter的渲染管道。
結束語
與其他框架相比,Flutter的速度非常快,但這部分是因為Flutter團隊提供了一個很好的API,它可以幫助你建立快速的應用程式;但有時會限制你使用會減慢你的應用程式速度的東西。正如Ian "Hixie" Hickson所說。
"我同意,做正確的剪裁是昂貴的。因此,我認為我們應該考慮是否可以重新設計框架,以完全避免剪接,除非明確要求。"
所以要記住費用,只有在真正需要的時候才使用剪裁,儘可能的使用優化後的小部件。
對我來說就是這樣! 我希望你喜歡我的第一篇英文文章。我通過給幾個編輯送百事可樂和多力多滋來賄賂他們,所以希望它能得到回報!
通過www.DeepL.com/Translator(免費版)翻譯