Animate graphic position change

4056
5
Jump to solution
03-18-2013 02:59 AM
Labels (1)
ae
by
Occasional Contributor II
The GpsLayer has a nice animation feature where the current location marker glide gracefully to new positions in the map. In my application I would like to have similar animations in a graphicslayer, so that when I update the position of a graphic, it will glide to this new position intead of just changing its location in the blink of a second.

Does anyone know if and how this can be achieved?
1 Solution

Accepted Solutions
MichaelBranscomb
Esri Frequent Contributor
Hi,

You should be able to replicate the smoother animation of the gps layer symbol by incrementally updating the geometry (still calling MoveTo) on a timer.

The GPSLayer source code is on codeplex so you can take a look at how it works - but I just put this together this basic sample based on the GPSLayer code with an easing function from http://www.gizma.com/easing/ to allow for some acceleration and deceleration.

Code behind:
using System.Windows; using ESRI.ArcGIS.Client; using ESRI.ArcGIS.Client.Symbols; using System.Windows.Media; using ESRI.ArcGIS.Client.Geometry; using System; using System.Windows.Threading;  namespace SmoothAnimation {     public partial class MainWindow : Window     {         GraphicsLayer _gl;         private DateTime animateStartTime;         private DispatcherTimer animationTimer;         private int animationDuration = 5000;         SimpleMarkerSymbol defaultSymbol;         SimpleMarkerSymbol animatingSymbol;                  public MainWindow()         {             InitializeComponent();              _gl = new GraphicsLayer();                        MyMap.Layers.Add(_gl);         }          private void MyMap_MouseClick(object sender, Map.MouseEventArgs e)         {             if (defaultSymbol == null)             {                 defaultSymbol = new SimpleMarkerSymbol() { Color = Brushes.Red, Size = 12, Style = SimpleMarkerSymbol.SimpleMarkerStyle.Circle };             }              if (animatingSymbol == null)             {                 animatingSymbol = new SimpleMarkerSymbol() { Color = Brushes.Blue, Size = 12, Style = SimpleMarkerSymbol.SimpleMarkerStyle.Circle };             }              if (_gl.Graphics.Count == 3)                 _gl.Graphics.Clear();              if (_gl.Graphics.Count == 2)                 _gl.Graphics.Remove(_gl.Graphics[0]);              _gl.Graphics.Add(new Graphic() { Symbol = defaultSymbol, Geometry = e.MapPoint });         }          private void AnimateButton_Click(object sender, RoutedEventArgs e)         {             MapPoint startPoint = _gl.Graphics[0].Geometry as MapPoint;             MapPoint finishPoint = _gl.Graphics[1].Geometry as MapPoint;              MapPoint animatingPoint = startPoint;             _gl.Graphics.Add(new Graphic() { Geometry = animatingPoint,Symbol = animatingSymbol });              animationTimer= new DispatcherTimer();             animationTimer.Interval = TimeSpan.FromMilliseconds(33);             animateStartTime = DateTime.Now;             animationTimer.Tick += (s, ex) =>              {                 double fraction = (DateTime.Now - animateStartTime).TotalMilliseconds / animationDuration;                 fraction = QuinticEasingInOut(fraction, 0, 1, 1);                 var x = (finishPoint.X - startPoint.X) * fraction + startPoint.X;                 var y = (finishPoint.Y - startPoint.Y) * fraction + startPoint.Y;                 animatingPoint.MoveTo(x, y);             };             animationTimer.Start();         }          public double QuinticEasingInOut(double t, double b, double c, double d)         {             t /= d / 2;             if (t < 1) return c / 2 * t * t * t * t * t + b;             t -= 2;             return c / 2 * (t * t * t * t * t + 2) + b;         }     } }


XAML:
<Window x:Class="SmoothAnimation.MainWindow"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"         xmlns:esri="http://schemas.esri.com/arcgis/client/2009"         Title="MainWindow" Height="350" Width="525">     <Grid>         <esri:Map x:Name="MyMap" UseAcceleratedDisplay="True" MouseClick="MyMap_MouseClick">             <esri:ArcGISTiledMapServiceLayer ID="World Topo Map"                         Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"/>         </esri:Map>         <Button Content="Animate" x:Name="AnimateButton" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="10"                  Click="AnimateButton_Click">         </Button>     </Grid> </Window>


Cheers

Mike

View solution in original post

0 Kudos
5 Replies
MichaelBranscomb
Esri Frequent Contributor
Hi,

You should use the Graphic.MoveTo method. The great thing about this method is that we allow it to be called on a background thread too (as opposed to other geometry changes or symbol changes which will raise an exception because the graphics layer / graphics collection has been created on the UI thread).

Checkout the sample http://resources.arcgis.com/en/help/runtime-wpf/samples/index.html#/Moving_Objects/02q2000000mm00000....

Cheers

Mike
ae
by
Occasional Contributor II
Thanks for your reply Mike, this is exactly what I need. Unfortunately I'm using an older version of the API (version 2.4), which does not include the MoveTo() method, so then I guess there is no other option than to implement this functionality myself. I do not have an EDN subscription so unfortunately there's  no chance of updating for me I guess.

Thanks anyway for your help!
0 Kudos
ae
by
Occasional Contributor II
I now had the opportunity to test out the Graphic.MoveTo method and unfortunately it does not work the way I hoped it would. Although it's a lot quicker, I do not get the animation that I was hoping for, where a graphic slides gracefully to the new position in the map (like in GpsLayer). I just wanted to clarify this to anyone who request similar functionality.

Cheers,

Aslak
0 Kudos
MichaelBranscomb
Esri Frequent Contributor
Hi,

You should be able to replicate the smoother animation of the gps layer symbol by incrementally updating the geometry (still calling MoveTo) on a timer.

The GPSLayer source code is on codeplex so you can take a look at how it works - but I just put this together this basic sample based on the GPSLayer code with an easing function from http://www.gizma.com/easing/ to allow for some acceleration and deceleration.

Code behind:
using System.Windows; using ESRI.ArcGIS.Client; using ESRI.ArcGIS.Client.Symbols; using System.Windows.Media; using ESRI.ArcGIS.Client.Geometry; using System; using System.Windows.Threading;  namespace SmoothAnimation {     public partial class MainWindow : Window     {         GraphicsLayer _gl;         private DateTime animateStartTime;         private DispatcherTimer animationTimer;         private int animationDuration = 5000;         SimpleMarkerSymbol defaultSymbol;         SimpleMarkerSymbol animatingSymbol;                  public MainWindow()         {             InitializeComponent();              _gl = new GraphicsLayer();                        MyMap.Layers.Add(_gl);         }          private void MyMap_MouseClick(object sender, Map.MouseEventArgs e)         {             if (defaultSymbol == null)             {                 defaultSymbol = new SimpleMarkerSymbol() { Color = Brushes.Red, Size = 12, Style = SimpleMarkerSymbol.SimpleMarkerStyle.Circle };             }              if (animatingSymbol == null)             {                 animatingSymbol = new SimpleMarkerSymbol() { Color = Brushes.Blue, Size = 12, Style = SimpleMarkerSymbol.SimpleMarkerStyle.Circle };             }              if (_gl.Graphics.Count == 3)                 _gl.Graphics.Clear();              if (_gl.Graphics.Count == 2)                 _gl.Graphics.Remove(_gl.Graphics[0]);              _gl.Graphics.Add(new Graphic() { Symbol = defaultSymbol, Geometry = e.MapPoint });         }          private void AnimateButton_Click(object sender, RoutedEventArgs e)         {             MapPoint startPoint = _gl.Graphics[0].Geometry as MapPoint;             MapPoint finishPoint = _gl.Graphics[1].Geometry as MapPoint;              MapPoint animatingPoint = startPoint;             _gl.Graphics.Add(new Graphic() { Geometry = animatingPoint,Symbol = animatingSymbol });              animationTimer= new DispatcherTimer();             animationTimer.Interval = TimeSpan.FromMilliseconds(33);             animateStartTime = DateTime.Now;             animationTimer.Tick += (s, ex) =>              {                 double fraction = (DateTime.Now - animateStartTime).TotalMilliseconds / animationDuration;                 fraction = QuinticEasingInOut(fraction, 0, 1, 1);                 var x = (finishPoint.X - startPoint.X) * fraction + startPoint.X;                 var y = (finishPoint.Y - startPoint.Y) * fraction + startPoint.Y;                 animatingPoint.MoveTo(x, y);             };             animationTimer.Start();         }          public double QuinticEasingInOut(double t, double b, double c, double d)         {             t /= d / 2;             if (t < 1) return c / 2 * t * t * t * t * t + b;             t -= 2;             return c / 2 * (t * t * t * t * t + 2) + b;         }     } }


XAML:
<Window x:Class="SmoothAnimation.MainWindow"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"         xmlns:esri="http://schemas.esri.com/arcgis/client/2009"         Title="MainWindow" Height="350" Width="525">     <Grid>         <esri:Map x:Name="MyMap" UseAcceleratedDisplay="True" MouseClick="MyMap_MouseClick">             <esri:ArcGISTiledMapServiceLayer ID="World Topo Map"                         Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"/>         </esri:Map>         <Button Content="Animate" x:Name="AnimateButton" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="10"                  Click="AnimateButton_Click">         </Button>     </Grid> </Window>


Cheers

Mike
0 Kudos
ae
by
Occasional Contributor II
Perfect! The sample code works like a charm! Thanks Mike!
0 Kudos