Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animation slows down when left margin becomes negative

I am trying to have a polygon move from completely off the left of the screen, across the screen, and then completely off the right of the screen, then back again.

I've gotten this working. BUT, for some reason, as soon as the left margin becomes negative, the animation suddenly slows down. As soon as the left margin becomes positive, it speeds up again.

Why does this happen? How can I stop it?

Here's the complete code that demonstrates this:

<Window x:Class="Geometry.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <PathGeometry x:Key="MyGeometry">
            <PathGeometry.Figures>
                <PathFigure>
                    <PathFigure.Segments>
                        <LineSegment Point="0.30,0" />
                        <LineSegment Point="0.70,1" />
                        <LineSegment Point="0.40,1" />
                        <LineSegment Point="0,0" />
                    </PathFigure.Segments>
                </PathFigure>
            </PathGeometry.Figures>
        </PathGeometry>
        <Storyboard x:Key="MovingAnimation">
            <ThicknessAnimationUsingKeyFrames RepeatBehavior="1:0:0" FillBehavior="HoldEnd" Storyboard.TargetName="_path" Storyboard.TargetProperty="Margin" >
                <DiscreteThicknessKeyFrame KeyTime="0:0:0" Value="-2.0,0,0,0"  />
                <LinearThicknessKeyFrame KeyTime="0:0:10" Value="1.0,0,0,0"  />
                <LinearThicknessKeyFrame KeyTime="0:0:20" Value="-2.0,0,0,0"  />
            </ThicknessAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="Window.Loaded">
            <BeginStoryboard Storyboard="{StaticResource MovingAnimation}" ></BeginStoryboard>
        </EventTrigger>
    </Window.Triggers>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Orientation="Horizontal">
            <Label>Margin:</Label>
            <TextBlock Text="{Binding ElementName=_path, Path=Margin.Left, StringFormat={}{0:0.#}}" />
        </StackPanel>
    <Canvas Name="_canvas" Grid.Row="1">
        <Border Margin="0" Width="1" Height="1" VerticalAlignment="Center" HorizontalAlignment="Center">
            <Border.RenderTransform>
                <ScaleTransform 
                    ScaleX="{Binding ElementName=_canvas, Path=ActualWidth}" 
                    ScaleY="{Binding ElementName=_canvas, Path=ActualHeight}"
                    CenterX="0"
                    CenterY="0">
                </ScaleTransform>
            </Border.RenderTransform>
            <Path 
                Name="_path" 
                Fill="#CCCCFF" 
                Data="{StaticResource MyGeometry}" 
                Width="1.0" 
                Height="1.0" 
                >
        </Path>
        </Border>
    </Canvas>
    </Grid>
</Window>
like image 366
Andrew Shepherd Avatar asked Jun 10 '13 11:06

Andrew Shepherd


People also ask

What is slow in and slow out in animation?

'Slow In' and 'Slow Out' – terms in animation that are used to describe an important principle: an object or a character in animated video start their movements out more slowly, then picks up speed and finishes with deceleration. The beginning and end of the movement are softened.

How do you make an animation move slower?

To speed up or slow down an animation, the easiest trick is to just add or remove ticks in between keyframes. To get the menu up, right click on the timeline. The ticks will be inserted at the point where you click on the timeline.

Do animations slow down website?

Too much motion can slow down the loading of a web page. Too many animations prevent visitors from knowing which parts of the page to focus on. Website animation doesn't always translate well to mobile, which leads to poor experiences.


2 Answers

Animating Margin property will trigger additional measure/arrange pass which in turn cause a bit more performance impact (though in this example it may not be noticeable). Animation of "render-only" properties on the other hand will not trigger layout re-arrangement and, thus, is more performance friendly.

Please, take a look at a bit easier way to do what, I suppose, you are want to get:

<Window x:Class="Geometry.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="518" Width="530">
<Window.Resources>
    <PathGeometry x:Key="MyGeometry">
        <PathGeometry.Figures>
            <PathFigure>
                <PathFigure.Segments>
                    <LineSegment Point="0.30,0" />
                    <LineSegment Point="0.70,1" />
                    <LineSegment Point="0.40,1" />
                    <LineSegment Point="0,0" />
                </PathFigure.Segments>
            </PathFigure>
        </PathGeometry.Figures>
    </PathGeometry>
    <Storyboard x:Key="MovingAnimation">
        <DoubleAnimationUsingKeyFrames RepeatBehavior="1:0:0" FillBehavior="HoldEnd" Storyboard.TargetName="_scaleTransform" Storyboard.TargetProperty="CenterX" >
            <LinearDoubleKeyFrame KeyTime="0:0:0" Value="1.2" />
            <LinearDoubleKeyFrame KeyTime="0:0:10" Value="-0.5" />
            <LinearDoubleKeyFrame KeyTime="0:0:20" Value="1.2" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Window.Resources>
<Window.Triggers>
    <EventTrigger RoutedEvent="Window.Loaded">
        <BeginStoryboard Storyboard="{StaticResource MovingAnimation}" ></BeginStoryboard>
    </EventTrigger>
</Window.Triggers>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0" Orientation="Horizontal">
        <Label>Margin:</Label>
        <TextBlock Text="{Binding ElementName=_scaleTransform, Path=CenterX, StringFormat={}{0:0.#}}" VerticalAlignment="Center" />
    </StackPanel>
<!--
    <Border Grid.Row="1" Margin="150" BorderBrush="Red" BorderThickness="1">
-->
    <Grid Name="_canvas" Grid.Row="1">
        <Path Name="_path" Fill="#CCCCFF" Data="{StaticResource MyGeometry}" 
              Width="1.0"
              Height="1.0"
              HorizontalAlignment="Center"
              VerticalAlignment="Center">
            <Path.RenderTransform>
                <ScaleTransform x:Name="_scaleTransform"
                    ScaleX="{Binding ElementName=_canvas, Path=ActualWidth}" 
                    ScaleY="{Binding ElementName=_canvas, Path=ActualHeight}"
                    CenterX="1.2"
                    CenterY="0.5">
                </ScaleTransform>
            </Path.RenderTransform>
        </Path>
    </Grid>
<!--
    </Border>
-->
</Grid>
</Window>
like image 30
Sevenate Avatar answered Oct 25 '22 14:10

Sevenate


I don't have an explanation why the negative margin animates slowly, but I found a workaround.

Instead of animating the Margin of the Path I animated the X value of a TranslateTransform on the Border object that contains the path. I had to put the TranslateTransform in front of the ScaleTransform so that the translation was applied before the scale. That allows you to use almost the same values in your animation that you used for the Margin.

    <Storyboard x:Key="MovingAnimation">
        <ThicknessAnimationUsingKeyFrames RepeatBehavior="1:0:0"  Storyboard.TargetName="_blank" Storyboard.TargetProperty="Margin" >
            <LinearThicknessKeyFrame KeyTime="0:0:0" Value="-1.5,0,0,0"  />
            <LinearThicknessKeyFrame KeyTime="0:0:10" Value="1,0,0,0"  />
            <LinearThicknessKeyFrame KeyTime="0:0:20" Value="-1.5,0,0,0"  />
        </ThicknessAnimationUsingKeyFrames>
    </Storyboard>

The ugly part is that I couldn't find a quick way to apply the values in the key frames directly to the X property of the TranslateTransform, so I cheated and used element binding and a placeholder Canvas object.

    <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="5">
        <TextBlock Margin="0,0,5,0" Text="Margin.Left:"/>
        <TextBlock Text="{Binding ElementName=_blank, Path=Margin.Left, StringFormat={}{0:0.#}}" />
    </StackPanel>
    <Canvas Name="_blank" />  <!--Placeholder object-->
    <Canvas Name="_canvas" Grid.Row="1">
        <Border Margin="0" Width="1" Height="1"
                Name="_border"
                VerticalAlignment="Center" HorizontalAlignment="Center">
            <Border.RenderTransform>
                <TransformGroup>
                    <TranslateTransform X="{Binding Margin.Left, ElementName=_blank}"/>
                    <ScaleTransform 
                        ScaleX="{Binding ElementName=_canvas, Path=ActualWidth}" 
                        ScaleY="{Binding ElementName=_canvas, Path=ActualHeight}"
                        CenterX="0"
                        CenterY="0">
                    </ScaleTransform>
                </TransformGroup>
            </Border.RenderTransform>

Even thought the animation is still being applied to a Margin and then to a TranslateTransform through element binding, the delay for a negative margin is gone.

I suspect that the negative margin delay has something to do with the Path being in the Border that was being scaled, but that is conjecture on my part.

If you can find a way to bind the KeyFrame values directly to the X property of the TranslateTransform, that would make this workaround much less ugly.

EDIT: Figured out the proper binding to use:

    <Storyboard x:Key="MovingAnimation2">
        <DoubleAnimationUsingKeyFrames RepeatBehavior="1:0:0"  Storyboard.TargetName="tt" Storyboard.TargetProperty="X" >
            <LinearDoubleKeyFrame KeyTime="0:0:0" Value="-1.5"  />
            <LinearDoubleKeyFrame KeyTime="0:0:5" Value="1"  />
            <LinearDoubleKeyFrame KeyTime="0:0:10" Value="-1.5"  />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>

This gets rid of the extra canvas placeholder object.

    <Canvas Name="_canvas" Grid.Row="1">
        <Border Margin="0" Width="1" Height="1"
                Name="_border"
                VerticalAlignment="Center" HorizontalAlignment="Center">
            <Border.RenderTransform>
                <TransformGroup>
                    <TranslateTransform x:Name="tt"/>
                    <ScaleTransform 
                        ScaleX="{Binding ElementName=_canvas, Path=ActualWidth}" 
                        ScaleY="{Binding ElementName=_canvas, Path=ActualHeight}"
                        CenterX="0"
                        CenterY="0">
                    </ScaleTransform>
                </TransformGroup>
            </Border.RenderTransform>
like image 198
Stewbob Avatar answered Oct 25 '22 13:10

Stewbob