Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Okay, so I'm working on a loading screen and I want to flare it up a bit.

Basically what I am trying to do is animate an object along path geometry data...I emphasize 'along' because keeping a fixed object along the path on a tangent is not what I would like to do

This is the best representation of what I am trying to do:

enter image description here

I can use a matrix transform to send this border element along the path but it winds up coming out as a tangential animation that moves and rotates with the path, but does not bend to fit the shape of the path...Here is an example of that:

<Border Background="Black" BorderBrush="Transparent" Width="20" Height="20">
<Border.RenderTransform>
    <MatrixTransform x:Name="MatrixT">
        <MatrixTransform.Matrix>
            <Matrix/>
        </MatrixTransform.Matrix>
    </MatrixTransform>
</Border.RenderTransform>
<Border.Triggers>
    <EventTrigger RoutedEvent="Border.Loaded">
        <BeginStoryboard>
            <Storyboard>
                <MatrixAnimationUsingPath Storyboard.TargetName="MatrixT" Storyboard.TargetProperty="Matrix" DoesRotateWithTangent="True" Duration="0:0:5" RepeatBehavior="Forever">
                    <MatrixAnimationUsingPath.PathGeometry>
                        <PathGeometry Figures="M201.1,50.501C201.1,78.138,178.737,100.501,151.1,100.501L150.799,100.501C123.162,100.501,114.933,77.834,100.8,50.501L100.8,50.5C86.666,23.167,78.437,0.5,50.8,0.5L50.5,0.5C22.863,0.5,0.500000000000014,22.863,0.500000000000014,50.5L0.500000000000014,50.501C0.500000000000014,78.138,22.863,100.501,50.5,100.501L50.8,100.501C78.437,100.501,86.666,77.834,100.8,50.501L100.8,50.5C114.933,23.167,123.162,0.5,150.799,0.5L151.1,0.5C178.736,0.5,201.1,22.863,201.1,50.501L201.1,50.501z" PresentationOptions:Freeze="True"/>
                    </MatrixAnimationUsingPath.PathGeometry>
                </MatrixAnimationUsingPath>
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</Border.Triggers>

I have come up with an alternative solution that looks really sharp, but I wanted to pitch this question out to the community to see if they have any ideas on how to accomplish this task (or if it is even possible)...I have done some extensive googling on the matter and have come up with nothing on how to accomplish this in an effective means.

The requirements:

  1. It must follow (or 'bend along') the path
  2. It must be able to scale in size without breaking the animation (many of the stroke animation representations I have seen of this can only operate at one size without re-configuring the animation properties)...a viewbox is perfectly acceptable to accomplish this

If the shape can be tapered and faded out on the trailing side that would be an even bigger plus (see the image above), but that might be more than is possible

EDIT: To clarify what I mean by 'bend'...I mean figure B below...figure A is the standard that I have traditionally seen:

enter image description here

share|improve this question
have you tried the PathListbox microsoft.com/design/toolbox/tutorials/pathlistbox – Jodha Jun 13 at 19:01
hmmm...thats an interesting control I hadn't seen yet, I actually have a few other uses for that...but it ultimately still doesn't allow for the object to bend/curve along the path, just tangentially rotate along it – Robert Petz Jun 13 at 19:57
this actually do what you are looking for just do a little google. – Jodha Jun 13 at 20:12
I still can't seem to find anything related to what I'm looking for with that...see the second picture I just added to the question...the PathListBox appears to allow you to place an array of display elements along a path, but it does not warp the appearance of the elements to bend along the path (outside of rotation/translate/scale) – Robert Petz Jun 13 at 20:24
add comment (requires an account with 50 reputation)

2 Answers

up vote 2 down vote accepted

Morphing your shape to a path would be very difficult in WPF. However there is a close approximation possible by animating two separate paths, and also animating two clipping regions at the same time.

Given below is XAML for an approximation of what you want. If you look carefully at the crossing point of the infinity symbol, you will notice a slight discontinuity in the smoothness of the shading during transition. This is because I was a little bit arbitrary in my setting of the Start, End, and Offset points for the LinearGradientBrush objects. A little work on those will smooth out that transition. You could even choose to animate the properties on the brushes to help with that.

<Window x:Class="AnimationTest.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"
        Background="#FF486CBF">
  <Viewbox>
    <Grid>
      <Canvas Width="50" Height="50"
              HorizontalAlignment="Left"
              VerticalAlignment="Top">
        <Canvas.Clip>
          <RectangleGeometry Rect="0,0,50,55">
            <RectangleGeometry.Transform>
              <TranslateTransform x:Name="_clip1"/>
            </RectangleGeometry.Transform>
          </RectangleGeometry>
        </Canvas.Clip>
        <Path StrokeStartLineCap="Round"
              StrokeEndLineCap="Round"
              StrokeThickness="10"
              RenderTransformOrigin="0.5,0.8571"
              Data="M 5,25 c 0,-25 40,-25 40,0">
          <Path.Stroke>
            <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
              <GradientStop Color="#FFFFFFFF" Offset="0"/>
              <GradientStop Color="#00FFFFFF" Offset="0.7"/>
            </LinearGradientBrush>
          </Path.Stroke>
          <Path.RenderTransform>
            <RotateTransform x:Name="_rot1" />
          </Path.RenderTransform>
          <Path.Triggers>
            <EventTrigger RoutedEvent="Path.Loaded">
              <BeginStoryboard>
                <Storyboard>
                  <DoubleAnimation From="360" To="0"
                                   Duration="0:0:3"
                                   RepeatBehavior="Forever"
                                   Storyboard.TargetName="_rot1"
                                   Storyboard.TargetProperty="Angle"/>
                  <DoubleAnimationUsingKeyFrames Storyboard.TargetName="_clip1"
                         Storyboard.TargetProperty="Y"
                         RepeatBehavior="Forever">
                    <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:1.5" Value="25"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:2.8" Value="55"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:4.5" Value="-30"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:5.8" Value="0"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:6" Value="0"/>
                  </DoubleAnimationUsingKeyFrames>
                </Storyboard>
              </BeginStoryboard>
            </EventTrigger>
          </Path.Triggers>
        </Path>
      </Canvas>

      <Canvas Width="50" Height="50"
              HorizontalAlignment="Left"
              VerticalAlignment="Top"
              Margin="40,0,0,0">
        <Canvas.Clip>
          <RectangleGeometry Rect="0,0,50,55">
            <RectangleGeometry.Transform>
              <TranslateTransform x:Name="_clip2"/>
            </RectangleGeometry.Transform>
          </RectangleGeometry>
        </Canvas.Clip>
        <Path StrokeStartLineCap="Round"
              StrokeEndLineCap="Round"
              StrokeThickness="10"
              RenderTransformOrigin="0.5,0.8571"
              Data="M 5,25 c 0,-25 40,-25 40,0">
          <Path.Stroke>
            <LinearGradientBrush StartPoint="1,0" EndPoint="0,0">
              <GradientStop Color="#FFFFFFFF" Offset="0"/>
              <GradientStop Color="#00FFFFFF" Offset="0.7"/>
            </LinearGradientBrush>
          </Path.Stroke>
          <Path.RenderTransform>
            <RotateTransform x:Name="_rot2" />
          </Path.RenderTransform>
          <Path.Triggers>
            <EventTrigger RoutedEvent="Path.Loaded">
              <BeginStoryboard>
                <Storyboard>
                  <DoubleAnimation From="0" To="360"
                         Duration="0:0:3"
                         RepeatBehavior="Forever"
                         Storyboard.TargetName="_rot2"
                         Storyboard.TargetProperty="Angle"/>
                  <DoubleAnimationUsingKeyFrames Storyboard.TargetName="_clip2"
                         Storyboard.TargetProperty="Y"
                         RepeatBehavior="Forever">
                    <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="55"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:1.5" Value="-30"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:2.8" Value="0"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:4.5" Value="25"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:5.8" Value="55"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:6" Value="55"/>
                  </DoubleAnimationUsingKeyFrames>
                </Storyboard>
              </BeginStoryboard>
            </EventTrigger>
          </Path.Triggers>
        </Path>
      </Canvas>
    </Grid>
  </Viewbox>
</Window>

An important point to note is that the clipping regions need to be applied to the Canvas objects. If they are applied to the Path objects, like you would normally do for an image, the clipping region then gets rotated along with the Path by the RenderTrasform. Not the desired effect.

share|improve this answer
CLIPPING! a-HA! That's the piece I was missing entirely...I kind of figured that I would need to fake the animation a tad, but my qualms with that were always due to the inability to make the morphed edges smooth...clipping very much is a solution for that. +1 kudos for the working code – Robert Petz Jun 14 at 15:53
add comment (requires an account with 50 reputation)

You could just put a lot of circles on the path and animate their diameters.

share|improve this answer
Technically, this is an answer, but it's not really ideal...there has to be a better way to accomplish this task...plus, unless you scale it super small you would have to have a LOT of circles in place to not see that it's not a smooth line – Robert Petz Jun 13 at 19:52
It depends on what you consider ideal and on how big the animation has to be.It might be too complicated to create a 'perfect' solution and fully acceptable to fake it. – Erno de Weerd Jun 13 at 21:07
Agreed, my current solution is using a set of 7 or 8 circles that follow the path and trail each other (with spaces between each other), somewhat like your solution...what I'm looking for though is a means of morphing the object using path geometry, which is useful for more than just animation...I have a pretty good feeling that this isn't possible out of the box however, so if I don't get any solutions to this in the next few days or so I'll take this as an answer to the question – Robert Petz Jun 13 at 22:06
Correct. "Out of the box" is not available. Following a path is possible: msdn.microsoft.com/en-us/magazine/dd263097.aspx morphing is a different beast: what happens when a segment is on a very sharp edge? – Erno de Weerd Jun 14 at 4:25
Yea there are definitely challenges with morphing, but it would still be useful to have that ability...sharp edges are an issue, but that is more-so an issue of improper usage of the feature than an imposibility...calculating the vertices of a spline along a sharp edge's sampled points and rendering it would be no different than rendering it along a smooth edge's sampled points (it would still require the exact same mathematical formula), but the end result would probably be undesired – Robert Petz Jun 14 at 16:00
show 2 more commentsadd comment (requires an account with 50 reputation)

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.