[UWP]使用Win2D的BorderEffect實現圖片的平鋪功能
1. WPF有,而UWP沒有的圖片平鋪功能
在WPF中只要將ImageSource的TileMode
屬性設定為Tile
即可實現圖片的平鋪,具體可見WPF的這些文件:
ImageBrush 類 (System.Windows.Media) _ Microsoft Docs
TileBrush 類 (System.Windows.Media) _ Microsoft Docs
TileBrush.TileMode 屬性 (System.Windows.Media) _ Microsoft Docs
WPF圖片平鋪功能我幾乎沒用過,只是作為基礎中的基礎知識記住了用法。我以為那麼基礎的功能在UWP肯定有,根本不用懷疑,所以當我在UWP中發現這麼基礎的東西居然沒有時真的嚇了一跳。
上圖左面是WPF版本的TileBrush
,右邊是UWP版本,可以看到UWP版本功能少了一大半。
這麼小的一個類,我覺得沒必要在這裡做簡化吧。幸好圖片平鋪可以使用Win2D裡的BorderEffect實現。
2. UWP中的圖片平鋪功能
<Grid>
<Rectangle x:Name="Background" />
</Grid>
假設有以上的XAML,要在名為Background
的元素上應用合成畫筆,首先引用Win2D.uwp nuget包,然後參考官方文件中 合成畫筆 的部分使用圖片建立一個合成畫筆:
var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; var imageBrush = compositor.CreateSurfaceBrush(); var loadedSurface = LoadedImageSurface.StartLoadFromUri(new Uri("ms-appx:///110Strawberry.png")); imageBrush.Surface = loadedSurface; imageBrush.Stretch = CompositionStretch.None;
現在就差建立一個SpriteVisual
並把它應用到Background的VisualTree上了,順便一提,是這張圖片:
不過要實現平鋪功能還需要建立一個BorderEffect:
var borderEffect = new BorderEffect { Source = new CompositionEffectSourceParameter("source") }; var effectFactory = compositor.CreateEffectFactory(borderEffect); var effectBrush = effectFactory.CreateBrush(); effectBrush.SetSourceParameter("source", imageBrush); var sprite = compositor.CreateSpriteVisual(); sprite.Brush = effectBrush; var backgroundVisual = ElementCompositionPreview.GetElementVisual(Background); var bindSizeAnimation = compositor.CreateExpressionAnimation("backgroundVisual.Size"); bindSizeAnimation.SetReferenceParameter("backgroundVisual", backgroundVisual); sprite.StartAnimation("Size", bindSizeAnimation); ElementCompositionPreview.SetElementChildVisual(Background, sprite);
總之BorderEffect
以imageBrush為Source,其它都保留預設值,將它它應用到Background的VisualTree上後效果如下:
這還不是我想要的平鋪效果。這是因為這時候ExtendX
和ExtendY
保持預設值的Clamp
,這個型別會讓BorderEffect重複影象邊緣的屬性。如果要實現我想要的平鋪需要將這兩個屬性設定為Wrap
:
borderEffect.ExtendX = CanvasEdgeBehavior.Wrap;
borderEffect.ExtendY = CanvasEdgeBehavior.Wrap;
居然不是從左上角開始平鋪的,和我的想法還是有出入,不過這種細節就算了。順便一提ExtendX
和ExtendY
還可以設定為Mirror
,效果如下:
3. 繫結Size
var backgroundVisual = ElementCompositionPreview.GetElementVisual(Background);
var bindSizeAnimation = compositor.CreateExpressionAnimation("backgroundVisual.Size");
bindSizeAnimation.SetReferenceParameter("backgroundVisual", backgroundVisual);
sprite.StartAnimation("Size", bindSizeAnimation);
最後順便提一下,上面的程式碼中有這麼一段程式碼沒介紹到,這是用來動態地設定SpriteVisual
的尺寸。ExpressionAnimation有一直執行和永不停止這兩個特性,建立ExpressionAnimation並在SpriteVisual
上執行動畫,實際上將SpriteVisual
的Size永遠地繫結為backgroundVisual 的Size的值。其實簡單地訂閱SizeChanged
事件也能達到這個效果,程式碼好像還少些。
4. 結語
這麼簡單的功能居然都要這麼多程式碼,或者有更簡單的實現?不過凡事都有要辯證地看,幸好它這麼複雜,又讓我水了一篇部落格。
Stack Overflow有給出其它的方案,可以參考一下。
5. 參考
Border effect - Win32 apps _ Microsoft Docs
合成畫筆 - Windows UWP applications _ Microsoft Docs
【Win2D】【譯】Win2D 快速入門 - h82258652 - 部落格園
基於關係的動畫 - Windows UWP applications Microsoft Docs
c# - UWP - How to tile a background image - Stack Overflow