1. 程式人生 > >[UWP]使用Win2D的BorderEffect實現圖片的平鋪功能

[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上後效果如下:

這還不是我想要的平鋪效果。這是因為這時候ExtendXExtendY保持預設值的Clamp,這個型別會讓BorderEffect重複影象邊緣的屬性。如果要實現我想要的平鋪需要將這兩個屬性設定為Wrap

borderEffect.ExtendX = CanvasEdgeBehavior.Wrap;
borderEffect.ExtendY = CanvasEdgeBehavior.Wrap;

居然不是從左上角開始平鋪的,和我的想法還是有出入,不過這種細節就算了。順便一提ExtendXExtendY還可以設定為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