UWP合成-将不透明蒙版应用于ListView的前30px [英] UWP Composition - Apply opacity mask to top 30px of a ListView

查看:93
本文介绍了UWP合成-将不透明蒙版应用于ListView的前30px的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何将效果应用于前30像素从完全透明到完全不透明的ListView?这个想法是,当您向下滚动时,顶部项逐渐消失。



我正在构建一个UWP应用程序,该设计要求启动ListView的前30px在不透明度0处过渡到不透明度1。从概念上讲,我在想象一个不透明蒙版,该蒙版将应用于SpriteVisual的顶部,但我不知道如何实现此目的。



我正在尝试使用Windows 10周年纪念版,Composition和Win2D。



编辑:图片可能会绘制1000个单词:





如果您查看此图片,则底部有两个内容元素-左和右下。尽管背景看起来是黑色的,但实际上是渐变色。如果检查两个元素的顶部,则它们对顶部变得更加透明,并通过背景显示。这就是我想要达到的效果。



编辑2:
为了显示我想要的效果的结果,这是一个如果使用重叠位图则显示效果的GIF:



标题背景图片为:
< a href = https://i.stack.imgur.com/6htbj.png rel = noreferrer>



较低的30px具有alpha渐变,并出现在gridview上方,从而使Grid View项目逐渐消失并在其下方滑动。



XAML布局如下:

 < Page 
x:Class = App14.MainPage
xmlns = http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x = http:// schemas.microsoft.com/winfx/2006/xaml
xmlns:local = using:App14
xmlns:d = http://schemas.microsoft.com/expression/blend/2008
xmlns:mc = http://schemas.openxmlformats.org/markup-compatibility/2006
mc:Ignorable = d>
< Grid>
< Grid.RowDefinitions>
< RowDefinition Height = 150 />
< RowDefinition Height = * />
< /Grid.RowDefinitions>

<图片来源= / Assets / background.png
Grid.Row = 0
Grid.RowSpan = 2
VerticalAlignment =顶部
Stretch = None />

< GridView Grid.Row = 1
Margin = 96,-30,96,96>
< GridView.Resources>
< Style TargetType = Image>
< Setter Property = Height Value = 400 />
< Setter Property = Width Value = 300 />
< Setter Property = Margin Value = 30 />
< / Style>
< /GridView.Resources>
< Image Source = Assets / 1.jpg />
< Image Source = Assets / 2.jpg />
< Image Source = Assets / 3.jpg />
< Image Source = Assets / 4.jpg />
< Image Source = Assets / 5.jpg />
< Image Source = Assets / 6.jpg />
< Image Source = Assets / 7.jpg />
< Image Source = Assets / 8.jpg />
< Image Source = Assets / 9.jpg />
< Image Source = Assets / 10.jpg />
< Image Source = Assets / 11.jpg />
< Image Source = Assets / 12.jpg />
< / GridView>

< ;!-内容上方的标题->

< Image Grid.Row = 0 Source = / Assets / header_background.png
Stretch = None />

< TextBlock x:Name = Title
Grid.Row = 0
FontSize = 48
Text = This is a Title
Horizo​​ntalAlignment = Center
VerticalAlignment = Center
Foreground = White />


< / Grid>

解决方案

因此,在Windows UI开发实验室问题列表上的



以下是XAML:

 < Grid x:Name = LayoutRoot> 

< Image x:Name = BackgroundImage
ImageOpened = ImageBrush_OnImageOpened
Source = ../ Assets / blue-star-background-wallpaper-3.jpg
Stretch = UniformToFill />

< GridView x:Name = Posters Margin = 200,48>
< GridView.Resources>
< Style TargetType = ListViewItem />
< Style TargetType = Image>
< Setter Property = Stretch Value = UniformToFill />
< Setter Property = Width Value = 300 />
< Setter Property = Margin Value = 12 />
< / Style>
< /GridView.Resources>
< GridViewItem>
< Image Source = Assets / Posters / 1.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 2.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 3.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 4.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 5.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 6.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 7.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 8.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 9.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 10.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 11.jpg />
< / GridViewItem>
< GridViewItem>
< Image Source = Assets / Posters / 12.jpg />
< / GridViewItem>
< / GridView>
< / Grid>

这是代码:

 私人布尔值_imageLoaded; 

//这是处理调整大小的初始方法
//我将研究表达式
私有异步void OnSizeChanged(object sender,SizeChangedEventArgs args)
{
if(!_imageLoaded)
{
return;
}
等待RenderOverlayAsync();
}

私有异步void ImageBrush_OnImageOpened(object sender,RoutedEventArgs e)
{
_imageLoaded = true;
等待RenderOverlayAsync();
}

//必须在打开背景图像后调用此方法,否则
//渲染目标位图为空
私有异步任务RenderOverlayAsync()
{
//设置组成
//(为了便于阅读,在这里排成一行,将是向前移动的成员变量)
var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
var canvasDevice = new CanvasDevice();
var compositionDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor,canvasDevice);

//确定需要剪切背景的区域
GeneralTransform gt = Posters.TransformToVisual(LayoutRoot);
Point elementPosition = gt.TransformPoint(new Point(0,0));

//我们的叠加高度和我们的海报控件一样宽,并且高30像素。
var overlayHeight = 30;
var areaToRender = new Rect(elementPosition.X,elementPosition.Y,Posters.ActualWidth,overlayHeight);

//从背景中捕获图像。
//
//注意:这只是< Image />元素,而不是网格。如果我们使用< Grid />,
//,我们还将拥有所有子元素,例如< GridView />以及-
//也无法达到目的!
//
//注意2:必须在打开背景图像后调用此方法,否则
//渲染目标位图为空
var bitmap = new RenderTargetBitmap( );
等待bitmap.RenderAsync(BackgroundImage);
var pixel =等待位图.GetPixelsAsync();

//我们需要显示DPI,因此我们知道在渲染位图时如何正确处理位图。
var dpi = DisplayInformation.GetForCurrentView()。LogicalDpi;

//将RenderTargetBitmap中的像素加载到CompositionDrawingSurface上
CompositionDrawingSurface uiElementBitmapSurface;
using(
//这是整个背景图像
//注意,我们在这里使用显示DPI。
var canvasBitmap = CanvasBitmap.CreateFromBytes(
canvasDevice, pixel.ToArray(),
位图.PixelWidth,
位图.PixelHeight,
DirectXPixelFormat.B8G8R8A8UIntNormalized,
dpi)

{
//我们创建一个可以在内存中绘制的表面。
//注意,我们正在使用叠加层的所需大小
uiElementBitmapSurface =
compositionDevice.CreateDrawingSurface(
new Size(areaToRender.Width,areaToRender.Height),
DirectXPixelFormat.B8G8R8A8UIntNormalized,DirectXAlphaMode.Premultiplied);
使用(var session = CanvasComposition.CreateDrawingSession(uiElementBitmapSurface))
{
// //这里我们仅绘制背景图像中希望用来覆盖
session.DrawImage的部分(canvasBitmap,0,0,areaToRender);
}
}

//将CompositionDrawingSurface分配给我想用来绘制相关SpriteVisual
的CompositionSurfacebrush var backgroundImageBrush = _compositor.CreateSurfaceBrush(uiElementBitmapSurface);

//加载我们的不透明蒙版图像。
//这是在诸如paint.net
的图形工具中创建的var opacityMaskSurface = await SurfaceLoader.LoadFromUri(new Uri( ms-appx:///Assets/OpacityMask.Png));

//使用ICompositionSurface创建包含要遮罩的背景图像的表面画笔
backgroundImageBrush.Stretch = CompositionStretch.UniformToFill;

//使用包含渐变不透明蒙版资产的ICompositionSurface创建表面画笔
CompositionSurfaceBrush opacityBrush = _compositor.CreateSurfaceBrush(opacityMaskSurface);
opacityBrush.Stretch = CompositionStretch.UniformToFill;

//创建maskbrush
compositionMaskBrush maskbrush = _compositor.CreateMaskBrush();
maskbrush.Mask = opacityBrush; //具有渐变不透明度蒙版资产
maskbrush.Source = backgroundImageBrush;的Surfacebrush //用要遮罩的背景图像的表面画笔

//创建适当大小,偏移量等的spritevisual。
SpriteVisual maskSprite = _compositor.CreateSpriteVisual();
maskSprite.Size = new Vector2((float)Posters.ActualWidth,overlayHeight);
maskSprite.Brush = maskbrush; //用maskbrush

进行绘制//将子画面外观设置为需要绘制在
顶部的XAML元素的子元素。ElementCompositionPreview.SetElementChildVisual(Posters,maskSprite);
}


How can I apply an effect to a ListView where the top 30px graduate from fully transparent to fully opaque? The idea is that as you scroll down, the top items gradually fade away.

I am building a UWP application where the design calls for the top 30px of a ListView to start at opacity 0 and transition to opacity 1. Conceptually I am imagining an Opacity Mask that would be applied to the top part of a SpriteVisual but I cannot work out how to achieve this.

I am attempting this using the the anniversary edition of Windows 10, Composition and Win2D.

Edit: a picture may paint a 1000 words:

If you look at this image, there are two content elements in the bottom-left and bottom-right. Although the background appears to be black, it is actually a gradient. If you examine the top of the two elements, they become more transparent towards the top, showing through the background. That is the effect I am trying to achieve.

Edit 2: In an attempt to show the outcome of the effect I am looking for, here is a GIF that shows the effect if I use overlaid bitmaps:

The header background image is:

The lower 30px has an alpha gradient and appears above the gridview giving the apparent effect of the grid view items fading out and sliding undernext the background.

The XAML layout looks like:

<Page
x:Class="App14.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App14"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="150" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Image Source="/Assets/background.png"
           Grid.Row="0"
           Grid.RowSpan="2"
           VerticalAlignment="Top"
           Stretch="None" />

    <GridView Grid.Row="1"
              Margin="96,-30,96,96">
        <GridView.Resources>
            <Style TargetType="Image">
                <Setter Property="Height" Value="400" />
                <Setter Property="Width" Value="300" />
                <Setter Property="Margin" Value="30" />
            </Style>
        </GridView.Resources>
        <Image Source="Assets/1.jpg" />
        <Image Source="Assets/2.jpg" />
        <Image Source="Assets/3.jpg" />
        <Image Source="Assets/4.jpg" />
        <Image Source="Assets/5.jpg" />
        <Image Source="Assets/6.jpg" />
        <Image Source="Assets/7.jpg" />
        <Image Source="Assets/8.jpg" />
        <Image Source="Assets/9.jpg" />
        <Image Source="Assets/10.jpg" />
        <Image Source="Assets/11.jpg" />
        <Image Source="Assets/12.jpg" />
    </GridView>

    <!-- Header above content -->

    <Image Grid.Row="0" Source="/Assets/header_background.png"
           Stretch="None" />

    <TextBlock x:Name="Title"
               Grid.Row="0"
               FontSize="48"
               Text="This Is A Title"
               HorizontalAlignment="Center"
               VerticalAlignment="Center"
               Foreground="White" />


</Grid>

解决方案

So with some assistance from @sohcatt on the Windows UI Dev Labs issues list, I have built a working solution.

Here is the XAML:

    <Grid x:Name="LayoutRoot">

    <Image x:Name="BackgroundImage"
           ImageOpened="ImageBrush_OnImageOpened"
           Source="../Assets/blue-star-background-wallpaper-3.jpg"
           Stretch="UniformToFill" />

    <GridView x:Name="Posters" Margin="200,48">
        <GridView.Resources>
            <Style TargetType="ListViewItem" />
            <Style TargetType="Image">
                <Setter Property="Stretch" Value="UniformToFill" />
                <Setter Property="Width" Value="300" />
                <Setter Property="Margin" Value="12" />
            </Style>
        </GridView.Resources>
        <GridViewItem>
            <Image Source="Assets/Posters/1.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/2.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/3.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/4.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/5.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/6.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/7.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/8.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/9.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/10.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/11.jpg" />
        </GridViewItem>
        <GridViewItem>
            <Image Source="Assets/Posters/12.jpg" />
        </GridViewItem>
    </GridView>
</Grid>

Here is the code:

        private bool _imageLoaded;

    // this is an initial way of handling resize 
    // I will investigate expressions
    private async void OnSizeChanged(object sender, SizeChangedEventArgs args)
    {
        if (!_imageLoaded)
        {
            return;
        }
        await RenderOverlayAsync();
    }

    private async void ImageBrush_OnImageOpened(object sender, RoutedEventArgs e)
    {
        _imageLoaded = true;
        await RenderOverlayAsync();
    }

    // this method must be called after the background image is opened, otherwise
    // the render target bitmap is empty
    private async Task RenderOverlayAsync()
    {
        // setup composition
        // (in line here for readability - will be member variables moving forwards)
        var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
        var canvasDevice = new CanvasDevice();
        var compositionDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, canvasDevice);

        // determine what region of the background we need to "cut out" for the overlay
        GeneralTransform gt = Posters.TransformToVisual(LayoutRoot);
        Point elementPosition = gt.TransformPoint(new Point(0, 0));

        // our overlay height is as wide as our poster control and is 30 px high
        var overlayHeight = 30;
        var areaToRender = new Rect(elementPosition.X, elementPosition.Y, Posters.ActualWidth, overlayHeight);

        // Capture the image from our background.
        //
        // Note: this is just the <Image/> element, not the Grid. If we took the <Grid/>, 
        // we would also have all of the child elements, such as the <GridView/> rendered as well -
        // which defeats the purpose!
        // 
        // Note 2: this method must be called after the background image is opened, otherwise
        // the render target bitmap is empty
        var bitmap = new RenderTargetBitmap();
        await bitmap.RenderAsync(BackgroundImage);
        var pixels = await bitmap.GetPixelsAsync();

        // we need the display DPI so we know how to handle the bitmap correctly when we render it
        var dpi = DisplayInformation.GetForCurrentView().LogicalDpi;

        // load the pixels from RenderTargetBitmap onto a CompositionDrawingSurface
        CompositionDrawingSurface uiElementBitmapSurface;
        using (
            // this is the entire background image
            // Note we are using the display DPI here.
            var canvasBitmap = CanvasBitmap.CreateFromBytes(
                canvasDevice, pixels.ToArray(),
                bitmap.PixelWidth,
                bitmap.PixelHeight,
                DirectXPixelFormat.B8G8R8A8UIntNormalized,
                dpi)
        )
        {
            // we create a surface we can draw on in memory.
            // note we are using the desired size of our overlay
            uiElementBitmapSurface =
                compositionDevice.CreateDrawingSurface(
                    new Size(areaToRender.Width, areaToRender.Height),
                    DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);
            using (var session = CanvasComposition.CreateDrawingSession(uiElementBitmapSurface))
            {
                // here we draw just the part of the background image we wish to use to overlay
                session.DrawImage(canvasBitmap, 0, 0, areaToRender);
            }
        }

        // assign CompositionDrawingSurface to the CompositionSurfacebrush with which I want to paint the relevant SpriteVisual
        var backgroundImageBrush = _compositor.CreateSurfaceBrush(uiElementBitmapSurface);

        // load in our opacity mask image.
        // this is created in a graphic tool such as paint.net
        var opacityMaskSurface = await SurfaceLoader.LoadFromUri(new Uri("ms-appx:///Assets/OpacityMask.Png"));

        // create surfacebrush with ICompositionSurface that contains the background image to be masked
        backgroundImageBrush.Stretch = CompositionStretch.UniformToFill;

        // create surfacebrush with ICompositionSurface that contains the gradient opacity mask asset
        CompositionSurfaceBrush opacityBrush = _compositor.CreateSurfaceBrush(opacityMaskSurface);
        opacityBrush.Stretch = CompositionStretch.UniformToFill;

        // create maskbrush
        CompositionMaskBrush maskbrush = _compositor.CreateMaskBrush();
        maskbrush.Mask = opacityBrush; // surfacebrush with gradient opacity mask asset
        maskbrush.Source = backgroundImageBrush; // surfacebrush with background image that is to be masked

        // create spritevisual of the approproate size, offset, etc.
        SpriteVisual maskSprite = _compositor.CreateSpriteVisual();
        maskSprite.Size = new Vector2((float)Posters.ActualWidth, overlayHeight);
        maskSprite.Brush = maskbrush; // paint it with the maskbrush

        // set the sprite visual as a child of the XAML element it needs to be drawn on top of
        ElementCompositionPreview.SetElementChildVisual(Posters, maskSprite);
    }

这篇关于UWP合成-将不透明蒙版应用于ListView的前30px的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆