Search code examples
c#xamlanimationuwpwindows-composition-api

uwp Expression node fade in animation


I am using the sample on Windows UI dev labs sample gallery. And I used ShyHeader example to put in my app, but I am not using exactly the same code but I actually edited the example according to own needs. My question is how can I use expression node to fade in a specific XAML element in correspondence to the scroll viewer, I am able to fade out an element with the scroll viewer. but I am not being able to fade in an element from opacity 0 -> opacity 1. here is my code.

<ScrollViewer x:Name="MyScrollViewer">
    <Grid>
        <local:MyAdaptiveView Margin="0,300,0,0"
                  x:Name="AllVideosGridView"/>
        <Grid x:Name="Header" Height="300" VerticalAlignment="Top">
            <FlipView x:Name="MainFlipView" 
            </FlipView>
            <Grid Background="Blue" Height="150" VerticalAlignment="Bottom" Opacity="0.5" Name="FrontGrid">

            </Grid>
        </Grid>
    </Grid>
</ScrollViewer>

page loaded method

the only important piece of code is only at the very end of this method, the last 4, 5 lines, you can see I am able to fade out element by doing 1- progresNode but my attempt to fade in another element ( frontVisual ) by doing 0+ progressNode doesn't work and frontVisual actually remains at 0 opacity even after I scroll. private void ShyView_Loaded(object sender, RoutedEventArgs e) { // Get the PropertySet that contains the scroll values from MyScrollViewer _scrollerPropertySet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(MyScrollViewer); _compositor = _scrollerPropertySet.Compositor;

        // Create a PropertySet that has values to be referenced in the ExpressionAnimations below
        _props = _compositor.CreatePropertySet();
        _props.InsertScalar("progress", 0);
        _props.InsertScalar("clampSize", 150);
        _props.InsertScalar("scaleFactor", 0.7f);

        // Get references to our property sets for use with ExpressionNodes
        var scrollingProperties = _scrollerPropertySet.GetSpecializedReference<ManipulationPropertySetReferenceNode>();
        var props = _props.GetReference();
        var progressNode = props.GetScalarProperty("progress");
        var clampSizeNode = props.GetScalarProperty("clampSize");
        var scaleFactorNode = props.GetScalarProperty("scaleFactor");

        // Create a blur effect to be animated based on scroll position
        var blurEffect = new GaussianBlurEffect()
        {
            Name = "blur",
            BlurAmount = 0.0f,
            BorderMode = EffectBorderMode.Hard,
            Optimization = EffectOptimization.Balanced,
            Source = new CompositionEffectSourceParameter("source")
        };

        var blurBrush = _compositor.CreateEffectFactory(
            blurEffect,
            new[] { "blur.BlurAmount" })
            .CreateBrush();

        blurBrush.SetSourceParameter("source", _compositor.CreateBackdropBrush());

        // Create a Visual for applying the blur effect
        _blurredBackgroundImageVisual = _compositor.CreateSpriteVisual();
        _blurredBackgroundImageVisual.Brush = blurBrush;
        _blurredBackgroundImageVisual.Size = new Vector2((float)Header.ActualWidth, (float)Header.ActualHeight);

        // Insert the blur visual at the right point in the Visual Tree
        ElementCompositionPreview.SetElementChildVisual(Header, _blurredBackgroundImageVisual);

        // Create and start an ExpressionAnimation to track scroll progress over the desired distance
        ExpressionNode progressAnimation = EF.Clamp(-scrollingProperties.Translation.Y / clampSizeNode, 0, 1);
        _props.StartAnimation("progress", progressAnimation);

        // Create and start an ExpressionAnimation to animate blur radius between 0 and 15 based on progress
        ExpressionNode blurAnimation = EF.Lerp(0, 15, progressNode);
        _blurredBackgroundImageVisual.Brush.Properties.StartAnimation("blur.BlurAmount", blurAnimation);

        // Get the backing visual for the header so that its properties can be animated
        Visual headerVisual = ElementCompositionPreview.GetElementVisual(Header);

        // Create and start an ExpressionAnimation to clamp the header's offset to keep it onscreen
        ExpressionNode headerTranslationAnimation = EF.Conditional(progressNode < 1, 0, -scrollingProperties.Translation.Y - clampSizeNode);
        headerVisual.StartAnimation("Offset.Y", headerTranslationAnimation);

        // Create and start an ExpressionAnimation to scale the header during overpan
        ExpressionNode headerScaleAnimation = EF.Lerp(1, 1.25f, EF.Clamp(scrollingProperties.Translation.Y / 50, 0, 1));
        headerVisual.StartAnimation("Scale.X", headerScaleAnimation);
        headerVisual.StartAnimation("Scale.Y", headerScaleAnimation);

        //Set the header's CenterPoint to ensure the overpan scale looks as desired
        headerVisual.CenterPoint = new Vector3((float)(Header.ActualWidth / 2), (float)Header.ActualHeight, 0);

        // Get the backing visual for the photo in the header so that its properties can be animated
        Visual photoVisual = ElementCompositionPreview.GetElementVisual(MainFlipView);

        // Create and start an ExpressionAnimation to opacity fade out the image behind the header
        ExpressionNode imageOpacityAnimation = 1 - progressNode;
        photoVisual.StartAnimation("opacity", imageOpacityAnimation);

        // Get the front visual for the photo in the header so that its properties can be animated
        Visual frontVisual = ElementCompositionPreview.GetElementVisual(FrontGrid);

        // Create and start an ExpressionAnimation to opacity fade out the image behind the header
        ExpressionNode imageOpacityAnimation2 = 0 + progressNode;
        frontVisual.StartAnimation("opacity", imageOpacityAnimation2);
    }

Note the behavior I actually want is that when I scroll down then FlipView should fade out and when I scroll up to the top it should fade in, which is working perfectly, but along with it I want FrontGrid to be exactly opposite, i.e: fade in on scroll down and fade out on scroll up.

Thanks in advance


Solution

  • Your expression looks OK.

    Note the Opacity you are animating with Composition is the Opacity of Visual. However, the Opacity of 0.5 you are setting on FrontGrid XAML is from UIElement. Doing so will break the Composition opacity expression animation.

    The fix is simple - Try getting the Visual of your FrontGrid right after InitializeComponent and set its Opacity to 0.5 there (i.e. frontVisual.Opacity = 0.5) instead of setting it in XAML.


    You will see this kind of "weird" behaviors starting from the Anniversary Update, due to a XAML-Composition Interop Behavior change.

    For a full explanation, please read this official document.

    In short, XAML doesn't know if Composition has changed the Opacity, it still thinks it should be 0.5 as it was last set. So it will try to override and cause the animation to fail. This happens to a few more properties like Offset and Size too.

    My advice is if you go Composition, try going Composition all the way. :)