开发者

Orbital animation

first of all I'll try to explain what I开发者_如何学Go'm trying to do. The task looks quite simple at first look, but it took me some time to realize its very complex. What I'm trying to do is simple animation with easing at the beginning and at the end - I know how to use easing, the hard part is that I'm trying to do something like Orbit - Elliptical orbit with lets say 5 rectangles attached to it. I want to move the rectangles along the Elliptical orbit (elliptical path) without changing the rotation angle of each rectangle.

I've tried with Path animation, but it seems that motion path animations don't support easing ? Am I wrong ? Second solution was to group the path and the rectangles and rotate the whole group, but this changes rectangles rotation angle too. Is there a simple way to do this ? Please point me to an article or something, or if you have a similar scenario please share the solution. Thanks.


Try this, it's great fun:

<Canvas Height="100" Width="100" RenderTransformOrigin="0.5,0.5">
    <Canvas.Triggers>
        <EventTrigger RoutedEvent="Canvas.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation From="0" To="360"
                                     RepeatBehavior="Forever"
                                     Duration="0:0:1"
                                     Storyboard.TargetProperty="RenderTransform.Angle">
                        <DoubleAnimation.EasingFunction>
                            <CubicEase EasingMode="EaseInOut"/>
                        </DoubleAnimation.EasingFunction>
                    </DoubleAnimation>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Canvas.Triggers>
    <Canvas.RenderTransform>
        <RotateTransform />
    </Canvas.RenderTransform>
    <Canvas.Resources>
        <Style TargetType="{x:Type Rectangle}">
            <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
            <Setter Property="RenderTransform" Value="{Binding RenderTransform.Inverse, RelativeSource={RelativeSource AncestorType=Canvas}}"/>
        </Style>
    </Canvas.Resources>
    <Rectangle Fill="Red" Height="20" Width="20" Canvas.Left="40" Canvas.Top="0"/>
    <Rectangle Fill="Green" Height="20" Width="20" Canvas.Left="40" Canvas.Top="80"/>
</Canvas>

Key Points:

  1. RotateTransform as RenderTransform on the Canvas which is animated.
  2. Implicit style via Canvas.Resources binds the RenderTransform of Rectangles to the inverse RotateTransform of the parent canvas.
  3. ???
  4. Profit!


Here's another approach...

What you want to do is not actually a rotation, but a translation along an elliptic path. The trouble is, a TranslateTransform is defined by an X and Y, not an angle and radius... But it's easier to animate an angle, so you have to convert polar coordinates to cartesian coordinates.

To do that, let's define two converters: SinConverter and CosConverter:

public class SinConverter : IValueConverter
{
    #region Implementation of IValueConverter

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            double angle = System.Convert.ToDouble(value);
            double angleRad = Math.PI * angle / 180;
            double radius = System.Convert.ToDouble(parameter);
            return radius * Math.Sin(angleRad);
        }
        catch
        {
            return Binding.DoNothing;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

public class CosConverter : IValueConverter
{
    #region Implementation of IValueConverter

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            double angle = System.Convert.ToDouble(value);
            double angleRad = Math.PI * angle / 180;
            double radius = System.Convert.ToDouble(parameter);
            return radius * Math.Cos(angleRad);
        }
        catch
        {
            return Binding.DoNothing;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Now, we need an angle property to animate: so we define a dummy RotateTransform in the resources, that will be the target of the animation.

Next, we apply a TranslateTransform on the "satellite", and we bind the X and Y to the angle, using our converters.

Eventually, we just need to create the animation itself that will animate the angle.

Here's the complete XAML:

<Window x:Class="WpfCS.Orbit"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:WpfCS"
        Title="Orbit"
        Height="300" Width="300">
    <Window.Resources>
        <my:SinConverter x:Key="sinConverter" />
        <my:CosConverter x:Key="cosConverter" />
        <RotateTransform x:Key="rotate" Angle="0" />
    </Window.Resources>
    <Grid>
        <Rectangle Width="30" Height="30" Fill="Blue">
            <Rectangle.RenderTransform>
                <TranslateTransform X="{Binding Path=Angle,
                                                Source={StaticResource rotate},
                                                Converter={StaticResource cosConverter},
                                                ConverterParameter=100}"
                                    Y="{Binding Path=Angle,
                                                Source={StaticResource rotate},
                                                Converter={StaticResource sinConverter},
                                                ConverterParameter=60}"/>
            </Rectangle.RenderTransform>
        </Rectangle>
        <Ellipse Width="5" Height="5" Fill="White" Stroke="Black" StrokeThickness="1" />
    </Grid>
    <Window.Style>
        <Style TargetType="Window">
            <Style.Triggers>
                <EventTrigger RoutedEvent="Loaded">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.Target="{StaticResource rotate}"
                                         Storyboard.TargetProperty="Angle"
                                         From="0" To="360" Duration="0:0:5"
                                         RepeatBehavior="Forever" />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Window.Style>
</Window>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜