Search code examples
c#animationuwpwindows-10-universalwindows-composition-api

UWP wincomposition: animate multiple images' offset


I try to animate multiple (different sized) images in one Grid control but only the biggest one is animated. The targeted property is "Offset.x".

        var container = new Grid();
        var compositor = ElementCompositionPreview.GetElementVisual(container).Compositor;
        var containerVisual = compositor.CreateContainerVisual();

        // ------
        // CLOUDS
        // ------
        var cloudImage1 = CreateCloudImage(60);
        var cloudVisual1 = ElementCompositionPreview.GetElementVisual(cloudImage1);

        var cloudImage2 = CreateCloudImage(70);
        var cloudVisual2 = ElementCompositionPreview.GetElementVisual(cloudImage2);

        var cloudImage3 = CreateCloudImage(50);
        var cloudVisual3 = ElementCompositionPreview.GetElementVisual(cloudImage3);

        container.Children.Add(cloudImage1);
        container.Children.Add(cloudImage2);
        container.Children.Add(cloudImage3);

        containerVisual.Children.InsertAtTop(cloudVisual1);
        containerVisual.Children.InsertAtTop(cloudVisual2);
        containerVisual.Children.InsertAtTop(cloudVisual3);

        // ----------
        // ANIMATIONS
        // ----------
        var offsetAnimation = CreateOffsetAnimation(compositor, 50, 6);
        cloudVisual1.StartAnimation("Offset.x", offsetAnimation);

        offsetAnimation.InsertKeyFrame(1f, -60);
        cloudVisual2.StartAnimation("Offset.x", offsetAnimation); // doesn't work

        ElementCompositionPreview.SetElementChildVisual(container, containerVisual);

        return container;

Helpers methods:

static ScalarKeyFrameAnimation CreateOffsetAnimation(Compositor compositor, float endKeyFrame, double duration) {
        var animation = compositor.CreateScalarKeyFrameAnimation();
        animation.InsertKeyFrame(0f, 0);
        animation.InsertKeyFrame(1f, endKeyFrame);
        animation.IterationBehavior = AnimationIterationBehavior.Forever;
        animation.Direction = AnimationDirection.Alternate;
        animation.Duration = TimeSpan.FromSeconds(duration);

        return animation;
}

static Image CreateCloudImage(double size) {
       var cloudBitmapImage = new BitmapImage(new Uri("ms-appx:///Assets/Icons/cloudy.png"));
       var cloudImageControl = new Image() {
            Source = cloudBitmapImage,
            Height = size,
            Width = size,
       };

       return cloudImageControl;
}

Note that if I make the 2nd image the biggest, it becomes the one which is animated.

Any idea on how to solve this ?

Thanks in advance.


Solution

  • Short answer: use a Canvas control to hold images.

    Full answer: I found the solution: the culprit was using the Grid control as a container for hosting my images. I think that somehow (maybe due to the center horizontal alignment set on its parent - not visible in the code showed) the Grid container was adding margins to my images which resulted in weird behaviors, as described in the question.

    To solve this, I added a Canvas control to hold my images and added this Canvas as a child of the Grid container.

            var container = new Grid();
            var compositor = ElementCompositionPreview.GetElementVisual(container).Compositor;
            var containerVisual = compositor.CreateContainerVisual();
    
            var canvas = new Canvas();
    
            // ------
            // CLOUDS
            // ------
            var cloudImage1 = CreateCloudImage(60);
            var cloudVisual1 = ElementCompositionPreview.GetElementVisual(cloudImage1);
    
            var cloudImage2 = CreateCloudImage(70);
            var cloudVisual2 = ElementCompositionPreview.GetElementVisual(cloudImage2);
    
            var cloudImage3 = CreateCloudImage(50);
            var cloudVisual3 = ElementCompositionPreview.GetElementVisual(cloudImage3);
    
            canvas.Children.Add(cloudImage1);
            canvas.Children.Add(cloudImage2);
            canvas.Children.Add(cloudImage3);
    
            containerVisual.Children.InsertAtTop(cloudVisual1);
            containerVisual.Children.InsertAtTop(cloudVisual2);
            containerVisual.Children.InsertAtTop(cloudVisual3);
    
            // ----------
            // ANIMATIONS
            // ----------
            var offsetAnimation = CreateOffsetAnimation(compositor, 50, 6);
            cloudVisual1.StartAnimation("Offset.x", offsetAnimation);
    
            offsetAnimation.InsertKeyFrame(1f, -60);
            cloudVisual2.StartAnimation("Offset.x", offsetAnimation); // work
    
            offsetAnimation.InsertKeyFrame(1f, 100);
            cloudVisual3.StartAnimation("Offset.x", offsetAnimation); // work
    
            ElementCompositionPreview.SetElementChildVisual(canvas, containerVisual);
    
            container.Children.Add(canvas);
            return container;
    

    (Replacing the Grid container with a Canvas container should work too).