1. 程式人生 > >win10 uwp 毛玻璃

win10 uwp 毛玻璃

原文: 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 安裝,如果速度太慢,推薦使用部落格園的映象

這個方法可以獲得控制元件的毛玻璃,但是不可以獲得視窗毛玻璃

接下來告訴大家如何做上圖的效果。

但是可以看到,上面的圖做了其他的,如拖動時顯示後面的圖片。為了顯示最短的程式碼,讓大家知道毛玻璃是如何做的,下面先來做效果。

第一步,獲得顯示的圖片

參見:win10 uwp 截圖 獲取螢幕顯示介面儲存圖片

於是在介面顯示一個圖片,介面的左邊就是圖片,右邊就是毛玻璃。之所以需要獲得圖片的截圖是因為毛玻璃需要輸入源,於是介面程式碼如下

         <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}">

參見:https://stackoverflow.com/questions/31987817/how-to-make-frosted-glass-effect-in-windows-10-universal-app

http://microsoft.github.io/Win2D/html/N_Microsoft_Graphics_Canvas_Effects.htm

(UWP)應用視窗實現毛玻璃效果 - 簡書

知識共享許可協議
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含連結:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。如有任何疑問,請與我聯絡