如何在Windows 10通用应用程序中制作磨砂玻璃效果?

4
那么,如何使用C#和XAML实现这样的效果呢?

http://i.stack.imgur.com/L2Zhc.gif


你尝试过什么吗?有没有写过一些代码或者做过其他的事情,但是没有成功? - Mickey
我在互联网上寻找类似的东西,但是有些例子会模糊图像,但不会重叠,而且它们与Windows 10无关,所以我甚至不知道从哪里开始。为了得到人们的帮助,我写在这里,但并不是让你们对我不屑一顾地回复。 - SnakeAce
2个回答

9
自从iOS 7引入了毛玻璃效果以来,我一直在尝试创建类似的东西。现在有了Win2D,使用WinRT创建类似的效果就变得简单多了。
首先,您需要从NuGet获取Win2D.uwp包。
我们的想法是基于图像源创建一个GaussianBlurEffect,并将其放在另一个白色掩模上方,以模仿毛玻璃的外观。
要准备好GaussianBlurEffect,您需要从Win2D库创建一个xaml控件CanvasControl。因此,UI结构如下:
<Grid x:Name="ImagePanel2" Width="356" Height="200" Margin="0,0,0,40" VerticalAlignment="Bottom">
    <Image x:Name="Image2" Source="Assets/Food.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>
    <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Frosted Glass" VerticalAlignment="Top" Foreground="#FF595959" Margin="12,12,0,0" FontWeight="Light" FontSize="26.667" FontStyle="Italic" TextLineBounds="Tight" />
</Grid>

注意,我已经为“Overlay”元素创建了一个“Clip”,因为我需要在将其向左滑动时减少“Rect”的第三个参数(即“宽度”),以制造“Overlay”似乎随着手指滑动的错觉。
后面的代码非常简单-
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)
{
    // reset the inital with of the Rect
    _x = (float)this.ImagePanel2.ActualWidth;
}

void Overlay_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
    // get the movement on X axis
    _x += (float)e.Delta.Translation.X;

    // keep the pan within the bountry
    if (_x > this.ImagePanel2.ActualWidth || _x < 0) return;

    // we clip the overlay to reveal the actual image underneath
    this.Clip.Rect = new Rect(0, 0, _x, this.ImagePanel2.ActualHeight);
}

void Overlay_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
    // reset the clip to show the full overlay
    this.Clip.Rect = new Rect(0, 0, this.ImagePanel2.ActualWidth, this.ImagePanel2.ActualHeight);
}

你可以进一步调整BlurAmount属性以及不透明度数值(0.9f),以获得你想要的确切效果。
另外需要注意的是,未来可能会有另一种(更好的?)方法来实现这一点。如果new Composition API在将来的版本中支持GaussianBlurEffect,我会更新答案。
你可以从这个GitHub repo中找到当前的工作样例。我在这里附上了一张图片以展示它的样子。 :)

enter image description here


哦,好的决定,谢谢,但这只是我需要的一半。那么有没有动态内容的选项呢?所以,我们有主页面和动态框架,就像这样:<Grid><SplitView><SplitView.Pane><Grid>overlay code, content</SplitView.Pane><Frame x:Name="dyn cont" /></SplitView></Grid>现在它只适用于背景图像。 - SnakeAce
据我所理解,一切都取决于图像,然后是:_bitmap = await CanvasBitmap.LoadAsync(sender, "here"); 那么,我们可以发送当前帧的屏幕吗?还是有其他方法? - SnakeAce
@SnakeAce,LoadAsync还接受IRandomAccessStream。因此,您可以使用RenderTargetBitmap从UI上的任何UIElement获取位图,然后将其转换为InMemoryRandomAccessStream。请参阅我的更新答案。 - Justin XL
1
在此处查看如何应用更高级的模糊效果:链接 - Justin XL

2
你可以查看Win2D类和Canvas Effects - 其中有一个GaussianBlurEffect可能会有帮助。这是参考文献: http://microsoft.github.io/Win2D/html/N_Microsoft_Graphics_Canvas_Effects.htm 以及GaussianBlurEffect: http://microsoft.github.io/Win2D/html/T_Microsoft_Graphics_Canvas_Effects_GaussianBlurEffect.htm 包括一些C#代码示例。
补充:如果你想知道如何使用win2D,我刚找到了一个方便的教程(我自己正在跟随 :)) http://blogs.msdn.com/b/uk_faculty_connection/archive/2014/09/05/win2d.aspx

没有使用过Win2D类,所以不能直接说。但是我在想,从性能的角度来看,使用SetWindowCompositionAttribute方法是否更好或更差呢?我知道它使用Interop,这可能是支持Win2D的一个因素,但它也似乎非常...本地化,可以这么说 :)(正如你可能听到的那样,我自己还没有测试过它,只是知道它,如果你自己还没有测试过,我明天会尝试一下,并在这里发布我的发现)示例:http://withinrafael.com/adding-the-aero-glass-blur-to-your-windows-10-apps/(对于“In your face”标志,我很抱歉) - ILOABN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接