win10 uwp 毛玻璃
毛玻璃在UWP很簡單,不會和WPF那樣傷效能。
本文告訴大家,如何在 UWP 使用 win2d 做毛玻璃。
毛玻璃可以使用 win2D 方法,也可以使用 Compositor 。
使用 win2d 得到軟體內控制元件毛玻璃,而使用 Compositor 可以獲得視窗毛玻璃。
先來說下如何使用 Compositor 做視窗毛玻璃,感覺小夥伴感興趣的是視窗毛玻璃。
Compositor 建立毛玻璃
先寫最簡單的頁面,只有一個 Grid, 給他名稱 GlassHost,這個控制元件用於顯示毛玻璃
<Grid x:Name="GlassHost"></Grid>
然後在建構函式使用InitializeFrostedGlass,這個函式用於在一個控制元件顯示毛玻璃
public MainPage()
{
InitializeComponent();
InitializeFrostedGlass(GlassHost);
}
private void InitializeFrostedGlass(UIElement glassHost)
{
Visual hostVisual = ElementCompositionPreview.GetElementVisual(glassHost);
Compositor compositor = hostVisual.Compositor;
var backdropBrush = compositor.CreateHostBackdropBrush();
var glassVisual = compositor.CreateSpriteVisual();
glassVisual.Brush = backdropBrush;
ElementCompositionPreview.SetElementChildVisual(glassHost, glassVisual);
var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size" );
bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);
glassVisual.StartAnimation("Size", bindSizeAnimation);
}
這樣就可以看到毛玻璃效果
這個程式碼是從 http://www.jianshu.com/p/3b49fd3d7edb 複製的
大概解釋一下, compositor.CreateHostBackdropBrush()
獲得 建立之前繪製視窗後面視覺效果的區域,然後把他新增到Grid就可以了。
但是模糊的玻璃可以看不到裡面控制元件,於是就把控制元件放在一個Grid 的最前,這樣看起來背景就是毛玻璃
<Grid > 最外層的 Grid 不要設定 BackGround
<Grid x:Name="GlassHost"></Grid> 把他放在最前
<ListView ItemsSource="{x:Bind AvaloniaCol}" IsItemClickEnabled="True" ItemClick="ListViewBase_OnItemClick" >
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="#FFFFFF" PointerPressed="UIElement_OnPointerPressed">
<TextBlock Text="{Binding}"></TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="新增" Click="ButtonBase_OnClick"></Button> 可以看到按鈕,是清晰的
</Grid>
如何去掉標題欄,上面的部落格也有說,於是我就不多說啦。
win2D
下面介紹使用 win2d 做毛玻璃
使用 win2D 方法,需要使用 Nuget 安裝,如果速度太慢,推薦使用部落格園的映象
這個方法可以獲得控制元件的毛玻璃,但是不可以獲得視窗毛玻璃
接下來告訴大家如何做上圖的效果。
但是可以看到,上面的圖做了其他的,如拖動時顯示後面的圖片。為了顯示最短的程式碼,讓大家知道毛玻璃是如何做的,下面先來做效果。
第一步,獲得顯示的圖片
於是在介面顯示一個圖片,介面的左邊就是圖片,右邊就是毛玻璃。之所以需要獲得圖片的截圖是因為毛玻璃需要輸入源,於是介面程式碼如下
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Margin="10 10 10 10">
必須把圖片的路徑修改為自己工程的路徑,需要在工程存在圖片
<Image x:Name="Image" Source="Assets/2017年5月31日 210702.jpg" Stretch="UniformToFill" />
</Grid>
<Grid Grid.Column="1" Margin="10 10 10 10">
<xaml:CanvasControl x:Name="Canvas" CreateResources="Canvas_CreateResources" Draw="Canvas_Draw" />
</Grid>
毛玻璃效果寫在 CanvasControl ,
需要對顯示截圖,把圖片做效果。然後把得到的效果顯示
但是在什麼時候截圖?也就是什麼時候才是截圖最好的時候?
我認為可以在 CreateResources 事件進行截圖,請看程式碼
void Canvas_CreateResources(CanvasControl sender, CanvasCreateResourcesEventArgs args)
{
args.TrackAsyncAction(CreateResourcesAsync(sender).AsAsyncAction());
}
async Task CreateResourcesAsync(CanvasControl sender)
{
// give it a little bit delay to ensure the image is load, ideally you want to Image.ImageOpened event instead
await Task.Delay(200); 這是等待圖片載入,因他發生在控制元件初始之後,而圖片載入發生在圖片控制元件初始的時候,但是圖片載入需要時間,所以這裡等待一下。我覺得這是比較差的方法
using (var stream = new InMemoryRandomAccessStream())
{
// get the stream from the background image
var target = new RenderTargetBitmap(); 這就是截圖
await target.RenderAsync(Image);
var pixelBuffer = await target.GetPixelsAsync();
var pixels = pixelBuffer.ToArray();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint) target.PixelWidth, (uint) target.PixelHeight, 96 如果 dpi 不是96 那麼這裡需要寫實際的,為了簡單,我就不寫如何獲得dpi, 96, pixels);
await encoder.FlushAsync();
stream.Seek(0);
// load the stream into our bitmap
_bitmap = await CanvasBitmap.LoadAsync(sender, stream);
}
}
第二步就是把圖片進行效果,程式碼很少
void Canvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
using (var session = args.DrawingSession)
{
var blur = new GaussianBlurEffect
{
BlurAmount = 50.0f, // increase this to make it more blurry or vise versa.
//Optimization = EffectOptimization.Balanced, // default value
//BorderMode = EffectBorderMode.Soft // default value
Source = _bitmap
};
session.DrawImage(blur, new Rect(0, 0, sender.ActualWidth, sender.ActualHeight),
new Rect(0, 0, _bitmap.SizeInPixels.Width, _bitmap.SizeInPixels.Height), 0.9f);
}
}
現在看起來就是
如果需要修改模糊,請把 BlurAmount 修改為你想要的
上面的程式碼就是主要的,接下來就是做上圖的效果
首先xaml程式碼:
<Grid x:Name="ImagePanel2" Width="356" Height="200" Margin="0,0,0,40" VerticalAlignment="Bottom">
<Image x:Name="Image2" Source="Assets/2017年5月31日 210702.jpg" Stretch="UniformToFill" />
<Grid x:Name="Overlay" ManipulationMode="TranslateX" ManipulationStarted="Overlay_ManipulationStarted" ManipulationDelta="Overlay_ManipulationDelta" ManipulationCompleted="Overlay_ManipulationCompleted" RenderTransformOrigin="0.5,0.5">
<Grid.Clip>
<RectangleGeometry x:Name="Clip" Rect="0, 0, 356, 200" />
</Grid.Clip>
<Rectangle x:Name="WhiteMask" Fill="White" />
<xaml:CanvasControl x:Name="Canvas" CreateResources="Canvas_CreateResources" Draw="Canvas_Draw" />
</Grid>
</Grid>
可以看到,這裡引用 CanvasControl ,還有很多程式碼需要寫在後面
void Canvas_CreateResources(CanvasControl sender, CanvasCreateResourcesEventArgs args)
{
args.TrackAsyncAction(CreateResourcesAsync(sender).AsAsyncAction());
}
async Task CreateResourcesAsync(CanvasControl sender)
{
// give it a little bit delay to ensure the image is load, ideally you want to Image.ImageOpened event instead
await Task.Delay(200);
using (var stream = new InMemoryRandomAccessStream())
{
// get the stream from the background image
var target = new RenderTargetBitmap();
await target.RenderAsync(this.Image2);
var pixelBuffer = await target.GetPixelsAsync();
var pixels = pixelBuffer.ToArray();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint) target.PixelWidth, (uint) target.PixelHeight, 96, 96, pixels);
await encoder.FlushAsync();
stream.Seek(0);
// load the stream into our bitmap
_bitmap = await CanvasBitmap.LoadAsync(sender, stream);
}
}
void Canvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
using (var session = args.DrawingSession)
{
var blur = new GaussianBlurEffect
{
BlurAmount = 50.0f, // increase this to make it more blurry or vise versa.
//Optimization = EffectOptimization.Balanced, // default value
//BorderMode = EffectBorderMode.Soft // default value
Source = _bitmap
};
session.DrawImage(blur, new Rect(0, 0, sender.ActualWidth, sender.ActualHeight),
new Rect(0, 0, _bitmap.SizeInPixels.Width, _bitmap.SizeInPixels.Height), 0.9f);
}
}
void Overlay_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
{
// 重新設定 _x
_x = (float) this.ImagePanel2.ActualWidth;
}
void Overlay_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
//獲得當前的x,用於下面計算
_x += (float) e.Delta.Translation.X;
//如果當前的x超過了,或者已經最小
if (_x > ImagePanel2.ActualWidth || _x < 0)
return;
//我們剪輯覆蓋,用於顯示下面的圖片
Clip.Rect = new Rect(0, 0, _x, this.ImagePanel2.ActualHeight);
}
void Overlay_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
// 重置剪輯顯示完整的覆蓋
Clip.Rect = new Rect(0, 0, this.ImagePanel2.ActualWidth, this.ImagePanel2.ActualHeight);
}
上面的程式碼就是獲得圖片,把圖片使用 GaussianBlurEffect 得到毛玻璃
實際程式碼做的就是 如下面顯示,做出毛玻璃效果,其他程式碼都是為了做剛才的圖
void Canvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
using (var session = args.DrawingSession)
{
var blur = new GaussianBlurEffect
{
BlurAmount = 50.0f, // increase this to make it more blurry or vise versa.
//Optimization = EffectOptimization.Balanced, // default value
//BorderMode = EffectBorderMode.Soft // default value
Source = _bitmap
};
session.DrawImage(blur, new Rect(0, 0, sender.ActualWidth, sender.ActualHeight),
new Rect(0, 0, _bitmap.SizeInPixels.Width, _bitmap.SizeInPixels.Height), 0.9f);
}
}
關於拖動使用裁剪顯示後面的圖,我就不多說了,實際程式碼看起來很多,但是不是很難,我就不說拉。
請看下面的效果,這就是不停修改 BlurAmount 得到。
程式碼很簡單,所以我就不說。
最簡單方法
當然,還有最簡單的程式碼,只需要一句話,請看文件
Acrylic material
因為不知道微軟是否還更改,所以我就不寫了。
為了說明程式碼的簡單,我需要給個例子,上面那麼長的程式碼,現在只需要一行
<Grid Background="{ThemeResource SystemControlAcrylicElementBrush}">
http://microsoft.github.io/Win2D/html/N_Microsoft_Graphics_Canvas_Effects.htm
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含連結:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。如有任何疑問,請與我聯絡。