來看光影流動之美
theme: v-green highlight: atom-one-dark
我正在參與掘金技術社群創作者簽約計劃招募活動,點選連結報名投稿。
前言
Flutter 的畫筆類 Paint
提供了很多圖形繪製的配置屬性,來供我們繪製更豐富多彩的圖形。前面幾篇我們介紹了 shader
屬性來繪製全屏漸變的聊天氣泡背景、漸變流動的邊框和毛玻璃效果的背景圖片,具體可以參考下面幾篇文章。
本篇我們引入一個 Paint
類新的屬性:maskFilter
,再結合之前的 shader
和動畫,看看能玩出什麼花樣。
MaskFilter 類簡介
MaskFilter 類,顧名思義是遮罩過濾器,也就是在繪製過程中給影象加一層遮罩效果,這個遮罩效果是通過某種變換函式實現的。Flutter 官方文件的說明如下,可以看出其實就是對點陣圖的顏色處理。
A mask filter to apply to shapes as they are painted. A mask filter is a function that takes a bitmap of color pixels, and returns another bitmap of color pixels. 遮罩過濾器應用於要繪製的形狀,其實就是一個函式,接收一個彩色畫素點陣圖,然後返回另一個彩色畫素點陣圖。
在 Flutter 裡面,目前只提供了一個 MaskFilter 的命名建構函式方式例項化,就是模糊效果。這個模糊效果和我們的圖片模糊有點類似,也是使用高斯模糊,只是多了一個模糊樣式引數。定義如下:
dart
const MaskFilter.blur(
this._style,
this._sigma,
)
關於這個方法的說明,為了便於理解,將官方的文件翻譯如下:
Creates a mask filter that takes the shape being drawn and blurs it. This is commonly used to approximate shadows. The
style
argument controls the kind of effect to draw; Thesigma
argument controls the size of the effect. It is the standard deviation of the Gaussian blur to apply. The value must be greater than zero. The sigma corresponds to very roughly half the radius of the effect in pixels. A blur is an expensive operation and should therefore be used sparingly. The arguments must not be null. 建立一個遮罩過濾器將要繪製的形狀進行模糊處理。通常用於實現近似陰影的效果。style
引數控制繪製的效果型別 (BlurStyle
列舉);sigma
引數控制效果的尺寸,實際就是使用的高斯模糊的標準差。sigma
的值必須大於0,這個值在畫素上,大致是效果範圍的半徑值。模糊處理比較耗效能,因此要有節制地使用。
因為效果有點類似陰影,而且比較耗效能,因此如果僅僅是要繪製陰影的話,官方推薦是使用 Canvas 的 drawShadow
方法替代這個效果。
MaskFilter 的幾種效果對比
看文件使用起來比較簡單,我們來看看 MaskFilter
的幾種不同的模糊樣式的區別。模糊樣式通過 style 引數控制,BlurStyle
列舉取值有以下四種。
normal
:形狀內外都會做模糊處理,可以用於繪製圖形表面投影的陰影效果。solid
:內部不模糊,外側模糊,會讓圖形看起來更明亮,類似熒光的效果。outer
:外側模糊,內部沒有東西,適用於繪製半透明圖形的陰影。inner
:外側不處理,內部模糊,看起來有種內發光的效果。
看文件說明我們是體會不到具體的呈現效果的,我們繪製同一個圖形的不同效果對比看看。
outer
型別有點奇怪,中間掏空了,不過看上去還挺酷的。我畫了一個不模糊的疊加上去,發現組合後的效果就和 solid
模式一樣。
上面繪製的程式碼如下。
dart
@override
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.style = PaintingStyle.fill;
var center = Offset(size.width / 2, size.height / 2);
var radius = 80.0;
paint.color = Colors.blue[400]!;
paint.maskFilter = MaskFilter.blur(BlurStyle.outer, 20.0);
canvas.drawCircle(center, radius, paint);
}
光影流動
通過繪製的效果發現,其實 MaskFilter 實現的效果有點像設計師給的各種發光效果,比如內發光、外發光。這時候,配合動畫可以玩點有趣的東西了。
光影流動效果1
我們利用之前實現的全屏漸變色聊天氣泡中的效果,通過動畫控制一個圓形上下移動看看會有什麼效果,感覺是一個彩色的光球在上升和下降。
上面效果的實現程式碼如下,基本的邏輯如下:
- 整個繪圖範圍通過
shader
預填充,使得圓形移動過程的填充色漸變變化,和我們聊天氣泡中的效果一樣; - 通過
transform
動畫控制顏色旋轉,使得繪製的圓形的填充顏色旋轉,看起來會有立體感; - 通過 maskFilter,設定為 solid 模式讓圓形有熒光的效果;
- 通過動畫控制圓形上升和下降。 ```dart void _drawMovingCircle(Canvas canvas, Size size, Paint paint) { var radius = 80.0; paint.shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.black87, Colors.purple, Colors.blue, Colors.green, Colors.yellow[500]!, Colors.orange, Colors.red[400]! ].reversed.toList(), tileMode: TileMode.clamp, transform: GradientRotation( animationValue * 2 * pi, ), ).createShader(Offset(0, 0) & size);
paint.maskFilter = MaskFilter.blur(BlurStyle.solid, 20.0); canvas.drawCircle( Offset(size.width / 2, size.height * animationValue), radius, paint); } ```
光影流動效果2
上面的效果是一個圓形的,我們換成多個並排的矩形來看看,這種效果感覺整個填充區域的顏色在不停流動變幻,就好像有霓虹燈照耀的感覺。
上面效果的實現程式碼如下,其實就是通過迴圈繪製了一排矩形,然後通過動畫控制上下移動位置。 ```dart void _drawMultiMovingRect(Canvas canvas, Size size, Paint paint) { paint.shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.black87, Colors.purple, Colors.blue, Colors.green, Colors.yellow[500]!, Colors.orange, Colors.red[400]! ].reversed.toList(), tileMode: TileMode.clamp, transform: GradientRotation( animationValue * 2 * pi, ), ).createShader(Offset(0, 0) & size);
paint.maskFilter = MaskFilter.blur(BlurStyle.solid, 20.0); var count = 10; for (var i = 0; i < count + 1; ++i) { canvas.drawRect( Offset(size.width / count * i, size.height * animationValue) & Size(size.width / count, size.width / count * 2), paint, ); } } ```
光影流動效果3
這一次我們使用 outer
型別的模糊效果,然後讓一串圓形沿螢幕對角線從收起到展開,再從展開到收起,效果如下所示,光束球發出來的時候,感覺就像是從左上角發了一個大招😁。
上面的實現程式碼如下所示,就是通過控制圓形的中心位置實現對角線移動的,間距則是隨著動畫值的增加而拉大,因此會有發射的效果。 ```dart void _drawMultiMovingCircle(Canvas canvas, Size size, Paint paint) { paint.shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.black87, Colors.purple, Colors.blue, Colors.green, Colors.yellow[500]!, Colors.orange, Colors.red[400]! ].reversed.toList(), tileMode: TileMode.clamp, transform: GradientRotation( animationValue * 2 * pi, ), ).createShader(Offset(0, 0) & size);
paint.maskFilter = MaskFilter.blur(BlurStyle.outer, 20.0); var count = 10; for (var i = 0; i < count + 1; ++i) { canvas.drawCircle( Offset(size.width * i / count * animationValue, size.height * i / count * animationValue), size.width / count, paint, ); } } ```
光影流動效果4:光影沿貝塞爾曲線流動
我們來把圖形通過貝塞爾曲線控制繪製位置,來畫一組圖形,就能夠實現光影沿著貝塞爾曲線流動的效果了。這裡我們沿著兩條首尾相接的貝塞爾曲線,繪製了一組正方形,看起來就像光影在一個閉合的圖形中來回穿梭一樣。
實現程式碼和之前的類似,只是矩形的位置通過貝塞爾曲線生成,如下所示。 ```dart void _drawRectsUsingBezier(Canvas canvas, Size size, Paint paint) { paint.shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.black87, Colors.purple, Colors.blue, Colors.green, Colors.yellow[500]!, Colors.orange, Colors.red[400]! ].reversed.toList(), tileMode: TileMode.clamp, transform: GradientRotation( animationValue * 2 * pi, ), ).createShader(Offset(0, 0) & size);
paint.maskFilter = MaskFilter.blur(BlurStyle.outer, 2); final height = 120.0; var p0 = Offset(0, size.height / 2 + height); var p1 = Offset(size.width / 4, size.height / 2 - height); var p2 = Offset(size.width * 3 / 4, size.height / 2 + height); var p3 = Offset(size.width, size.height / 2 - height); var count = 150; var squareSize = 20.0; for (var t = 1; t <= count; t += 1) { var curvePoint = BezierUtil.get3OrderBezierPoint(p0, p1, p2, p3, t / count);
canvas.drawRect(
curvePoint & Size(squareSize, squareSize),
paint,
);
}
for (var t = 1; t <= count; t += 1) { var curvePoint = BezierUtil.get3OrderBezierPoint(p3, p1, p2, p0, t / count);
canvas.drawRect(
curvePoint & Size(squareSize, squareSize),
paint,
);
} } ```
總結
本篇介紹了 Flutter 畫筆類 Painter
的 MaskFilter
的使用,MaskFilter
的應用效果就是讓形狀能有類似發光的效果。我們結合之前的幾篇文章用到的繪製效果完成了一場“光影秀”,視覺上看起來還是很美的。這也是開發過程中繪圖的樂趣之一,看著自己畫出來很美的效果也還是很有成就感的。
本篇原始碼已提交至:繪圖相關程式碼,檔名為:mask_filter_demo.dart
。
我是島上碼農,微信公眾號同名,這是Flutter 入門與實戰的專欄文章,提供體系化的 Flutter 學習文章。對應原始碼請看這裡:Flutter 入門與實戰專欄原始碼。如有問題可以加本人微信交流,微訊號:
island-coder
。👍🏻:覺得有收穫請點個贊鼓勵一下!
🌟:收藏文章,方便回看哦!
💬:評論交流,互相進步!
- 屌炸天!國外同行這樣用Chat GPT提高Flutter開發的效率!
- Flutter 增強版的頁面懸浮按鈕(FloatingActionButton)
- 介紹一個令強迫症討厭的小紅點元件
- 我用了幾行程式碼就搞定了介面變灰效果
- 不就是一個空白頁,有必要那麼講究嗎?
- 花裡胡哨的文字特效,你學會了嗎?
- 這一篇讓你搞定 Flutter 的資料表格
- 用 Flutter 輕鬆做個紅包介面
- 沉浸式彈層越來越多,開發該怎麼做好彈層體驗?
- 列表的載入過程很重要的!
- 這個表單打死我也不填!
- 例項講述開發中的圖片使用者體驗要點
- C 位出道按鈕的自我獨白
- Flutter 繪製3D效果動畫
- 封裝一個有趣的 Loading 元件
- 普通的載入千篇一律,有趣的 loading 萬里挑一
- 由點匯聚成字的動效炫極了
- 給滅霸點顏色看看
- 來看光影流動之美
- Flutter 實現背景圖片毛玻璃效果