User avatar
goldsaucer
Topic Author
Posts: 12
Joined: 06 Dec 2018, 17:53

Triggering animation whenever screen is shown

10 Dec 2018, 17:08

I figured out to make a very simple animation. A UI element "bouncing" into the screen.
            <Border x:Name="MyBorder">
                <Border.Triggers>
                    <EventTrigger RoutedEvent="Border.Loaded">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    From="-100"
                                    To="0"
                                    Duration="0:0:1"
                                    Storyboard.TargetName="MyBorder"
                                    Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
                                >
                                    <DoubleAnimation.EasingFunction>
                                        <BackEase Amplitude="0.8" EasingMode="EaseInOut" />
                                    </DoubleAnimation.EasingFunction>
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Border.Triggers>

                <Border.RenderTransform>
                    <TranslateTransform X="0" Y="0"/>
                </Border.RenderTransform>
                <!-- ... -->
            </Border>
So far so good. But right now I'm using Border.Loaded to trigger it. Which of course only happens once.
But I'd like this to be triggered everytime I go back to this screen (without having to reload it every time).

I looked through the available events and I couldn't quite find one.
Is there another way to trigger this programmatically perhaps?
 
User avatar
sfernandez
Site Admin
Posts: 1415
Joined: 22 Dec 2011, 19:20

Re: Triggering animation whenever screen is shown

12 Dec 2018, 11:26

In this scenario, where you want to animate a property of an element, you can use a DataTrigger in a Style:
<Border x:Name="MyBorder">
    <Border.Style>
        <Style TargetType="Border">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsEnabled, ElementName=MyBorder}" Value="True">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    From="-100"
                                    To="0"
                                    Duration="0:0:1"
                                    Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
                                >
                                    <DoubleAnimation.EasingFunction>
                                        <BackEase Amplitude="0.8" EasingMode="EaseInOut" />
                                    </DoubleAnimation.EasingFunction>
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Border.Style>
    <Border.RenderTransform>
        <TranslateTransform X="0" Y="0"/>
    </Border.RenderTransform>
    <!-- ... -->
</Border>
You can modify border's IsEnabled property to trigger the animation.

To trigger an animation from code you just need to store it as a resource so you can grab it later:
<Grid.Resources>
    <Storyboard x:Key="BounceAnim">
        <DoubleAnimation
            From="-100"
            To="0"
            Duration="0:0:1"
            Storyboard.TargetName="MyBorder"
            Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
        >
            <DoubleAnimation.EasingFunction>
                <BackEase Amplitude="0.8" EasingMode="EaseInOut" />
            </DoubleAnimation.EasingFunction>
        </DoubleAnimation>
    </Storyboard>
</Grid.Resources>
If you are using C# SDK or Unity, having a reference to the root UI element where the Storyboard resource and target Border are defined, you can do:
Storyboard bounceAnim = (Storyboard)root.FindResource("BounceAnim");
Border myBorder = (Border)root.FindName("MyBorder");
bounceAnim.Begin(myBorder);
In C++ it will be pretty much the same:
Storyboard* bounceAnim = root->FindResource<Storyboard>("BounceAnim");
Border* myBorder = root->FindName<Border>("MyBorder");
bounceAnim->Begin(myBorder);
 
User avatar
goldsaucer
Topic Author
Posts: 12
Joined: 06 Dec 2018, 17:53

Re: Triggering animation whenever screen is shown

13 Dec 2018, 10:08

Ah I see. I wonder why I hadn't thought the DataTrigger myself before. But especially being able to trigger the animation by making it a resource and using that in the code is super helpful. Thanks a lot!
 
User avatar
sfernandez
Site Admin
Posts: 1415
Joined: 22 Dec 2011, 19:20

Re: Triggering animation whenever screen is shown

13 Dec 2018, 10:35

You're welcome.
 
User avatar
goldsaucer
Topic Author
Posts: 12
Joined: 06 Dec 2018, 17:53

Re: Triggering animation whenever screen is shown

14 Dec 2018, 12:47

This works fine with hard coded values. Do you know how to make it work with dynamic values / bindings, though?

I tried to do the following but get the exception below:
                <Border.Style>
                    <Style TargetType="{x:Type Border}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding SlideInUI}" Value="True">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <DoubleAnimation
                                                From="{Binding ActualHeight, ElementName=MyBorder}"
                                                To="0"
                                                Duration="0:0:1"
                                                Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
                                            >
                                                <DoubleAnimation.EasingFunction>
                                                    <BackEase Amplitude="0.8" EasingMode="EaseInOut" />
                                                </DoubleAnimation.EasingFunction>
                                            </DoubleAnimation>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </DataTrigger.EnterActions>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
NoesisException: Can't Freeze BeginStoryboard.Storyboard
Rethrow as NoesisException
Noesis.Error.Check () (at Assets/NoesisGUI/Plugins/API/Core/NoesisError.cs:17)
Noesis.Texture.Noesis_WrapTexture_ (System.IntPtr texture, System.Int32 width, System.Int32 height, System.Int32 numLevels) (at Assets/NoesisGUI/Plugins/NoesisTexture.cs:22)
Noesis.Texture.WrapTexture (System.Object texture, System.IntPtr nativePointer, System.Int32 width, System.Int32 height, System.Int32 numLevels) (at Assets/NoesisGUI/Plugins/NoesisTexture.cs:11)
Noesis.TextureSource..ctor (UnityEngine.Texture2D texture) (at Assets/NoesisGUI/Plugins/NoesisTextureSource.cs:12)
AskutronGUI.Views.BaseControl`1[TM].TextureSourceForAction (Goldsaucer.Askutron.Players.PlayerAction action) (at Assets/Views/BaseControl.cs:151)
AskutronGUI.Views.BaseControl`1[TM].InitButtonTextures () (at Assets/Views/BaseControl.cs:88)
AskutronGUI.Views.BaseControl`1[TM].OnInitialized (System.Object sender, Noesis.EventArgs args) (at Assets/Views/BaseControl.cs:69)
Noesis.FrameworkElement.RaiseInitialized (System.IntPtr cPtr, System.IntPtr sender, System.IntPtr e) (at Assets/NoesisGUI/Plugins/API/Proxies/FrameworkElement.cs:247)
Rethrow as NoesisException
Noesis.Error.Check () (at Assets/NoesisGUI/Plugins/API/Core/NoesisError.cs:17)
Noesis.View.Noesis_View_Create_ (System.Runtime.InteropServices.HandleRef content) (at Assets/NoesisGUI/Plugins/API/Core/NoesisView.cs:402)
Noesis.View..ctor (Noesis.FrameworkElement content) (at Assets/NoesisGUI/Plugins/API/Core/NoesisView.cs:386)
NoesisView.CreateView (Noesis.FrameworkElement content) (at Assets/NoesisGUI/Plugins/NoesisView.cs:1036)
NoesisView.LoadXaml (System.Boolean force) (at Assets/NoesisGUI/Plugins/NoesisView.cs:340)
NoesisView.OnEnable () (at Assets/NoesisGUI/Plugins/NoesisView.cs:398)
P.S. I'm actually trying to start from minus the height, but I have yet to write a custom converter for that.

P.P.S. Nevermind, it seems you can't use bindings in storyboards. But there is a (somewhat complicated) work around. See https://stackoverflow.com/questions/218 ... 5#14164245.

P.P.P.S. Ok that doesn't work after all because there is no IMultiValueConverter in Noesis. The simple approach (which doesn't work for control templates) may still work, though: https://stackoverflow.com/questions/515 ... -animation

P4.S. The simple approach doesn't work either, unfortuantely. Still getting the can't freeze error.
 
User avatar
goldsaucer
Topic Author
Posts: 12
Joined: 06 Dec 2018, 17:53

Re: Triggering animation whenever screen is shown

14 Dec 2018, 14:26

I'm actually quite baffled that this seems to be such a complicated issue to have dynamic values for an animation both in WPF and noesis.
I mean in WPF it's still possible although using a hack. But in noesis it seems to be simply impossible (due to the lack of MultiBindings). :S

Am I doing something wrong? Does no one ever want to animate things based on their actual dynamic dimensions rather than some hard coded values?
Even if use hard coded values I still have to scale those to work on different display sizes. Or is there a noesis feature I'm missing where hard coded values for widths, heights etc. are scaled automatically so that it looks the same on any screen? Right now I'm actually using bindings for that doing the scaling myself...
 
User avatar
goldsaucer
Topic Author
Posts: 12
Joined: 06 Dec 2018, 17:53

Re: Triggering animation whenever screen is shown

14 Dec 2018, 15:54

Ok I found a programmatic solution. When entering the screen I trigger the animation via code as shown by sfernandez above.
The trick is to set the render transform of the element to its current ActualHeight/Width before starting the animation and not setting a `from` in the animation, because then it will just start where ever we put it using the render transform.
public void AnimateBorder()
{
            // control is my currently shown screen which is a UserControl
            Border border = Control.FindName("MyBorder") as Border;
            Storyboard anim = (Storyboard) Control.FindResource("MyAnimation");
            TranslateTransform transform = new TranslateTransform(0, -2 * border.ActualHeight);

            border.RenderTransform = transform;
            anim.Begin(border);
}
 
User avatar
sfernandez
Site Admin
Posts: 1415
Joined: 22 Dec 2011, 19:20

Re: Triggering animation whenever screen is shown

14 Dec 2018, 17:06

The error about "Can't Freeze BeginStoryboard.Storyboard" is a problem we already fixed for the next version.

Have you tried to bind the transform property in the xaml itself using a converter?
<Border x:Name="MyBorder" ...>
  <Border.RenderTransform>
    <TranslateTransform Y="{Binding ActualHeight, ElementName=MyBorder, Converter={StaticResource MultiplierConverter}, ConverterParameter=-2}"/>
  </Border.RenderTransform>
  ...
</Border>
Provided that you define a converter class called MultiplierConverter that accepts a param to multiply the bound value, and that you don't set the From value in the animation, this scenario should work.

Could you try that and let us know if find anything wrong?
 
User avatar
goldsaucer
Topic Author
Posts: 12
Joined: 06 Dec 2018, 17:53

Re: Triggering animation whenever screen is shown

14 Dec 2018, 22:22

Thanks, binding the actual height to the render transform's Y directly in XAML does work too!
 
User avatar
sfernandez
Site Admin
Posts: 1415
Joined: 22 Dec 2011, 19:20

Re: Triggering animation whenever screen is shown

17 Dec 2018, 11:55

Great, thanks for your collaboration.

Who is online

Users browsing this forum: Google [Bot] and 4 guests