Hi,
I have a root Canvas and in that another Canvas, which contains several objects (rectangle, images...). I want to centre a clicked object and scale it to an almost screen filling size. If there is an object directly in a neighbourhood of another I want to pan to it by clicking. The important thing is that all transformations and scales should be animated and fluid, so the user gets nice feedback of the motion.
My problem is, that if already scaled the canvas to let's say 2.3, with the next scaling, it doesn't zoom fluid from 2.3 to 5. Either there is only one abrupt transformation or the scaling goes back to 1 before scaling deeper to 5.
Do you know a better way for zooming and panning with Avalon
Is there a failure or wrong strategy
I just started with Avalon....here's my first try:
<Canvas Name="canvasRoot" Background="DarkGoldenrod" >
<Canvas MouseLeftButtonDown="_MouseLeftButtonDown" MouseRightButtonDown="_MouseRightButtonDown" Name="canvasAll" Width="500" Height="500" Background="Black">
<Rectangle Height="50" Width="50" Fill="Green" Stroke="Red" StrokeThickness="2"
Canvas.Left="225" Canvas.Top="0">
</Rectangle>
<Rectangle Height="50" Width="50" Fill="Green" Stroke="Red" StrokeThickness="2"
Canvas.Left="450" Canvas.Top="0">
</Rectangle>
</Canvas>
</Canvas>
public partial class Window1 : Window
{
double currentDx = 0;
double currentDy = 0;
double currentCx = 0;
double currentCy = 0;
double currentScaleFacX = 1;
double currentScaleFacY = 1;
double offset = 100;
public Window1()
{
InitializeComponent();
}
private void _MouseLeftButtonDown(object sender, MouseEventArgs e)
{
FrameworkElement obj = (FrameworkElement)e.OriginalSource;
double oW = obj.ActualWidth;
double oH = obj.ActualHeight;
Matrix ma = ((Transform)obj.TransformToAncestor(canvasAll)).Value;
Point pointA = new Point(0, 0) * ma;
Matrix mr = ((Transform)obj.TransformToAncestor(canvasRoot)).Value;
Point pointR = new Point(0, 0) * mr;
Storyboard myStory = new Storyboard();
// compute translate x,y
double tx = canvasRoot.ActualWidth / 2 - (oW / 2) - pointA.X;
double ty = canvasRoot.ActualHeight / 2 - (oH / 2) - pointA.Y;
// translate X
DoubleAnimation transAnimX = new DoubleAnimation();
transAnimX.Duration = new Duration(TimeSpan.FromMilliseconds(500));
transAnimX.From = currentDx;
transAnimX.To = currentDx = tx;
myStory.Children.Add(transAnimX);
Storyboard.SetTargetName(transAnimX, canvasAll.Name);
Storyboard.SetTargetProperty(transAnimX, new PropertyPath("(0).(1)[1].(2)", Canvas.RenderTransformProperty, TransformGroup.ChildrenProperty, TranslateTransform.XProperty));
// translate Y
DoubleAnimation transAnimY = new DoubleAnimation();
transAnimY.Duration = new Duration(TimeSpan.FromMilliseconds(500));
transAnimY.From = currentDy;
transAnimY.To = currentDy = ty;
myStory.Children.Add(transAnimY);
Storyboard.SetTargetName(transAnimY, canvasAll.Name);
Storyboard.SetTargetProperty(transAnimY, new PropertyPath("(0).(1)[1].(2)", Canvas.RenderTransformProperty, TransformGroup.ChildrenProperty, TranslateTransform.YProperty));
// compute scale
double scaleFac = Math.Min((canvasRoot.ActualWidth - offset) / oW, (canvasRoot.ActualHeight - offset) / oH);
// set scale center
scaleAll.CenterX = currentCx = pointA.X + oW/2;
scaleAll.CenterY = currentCy = pointA.Y + oH / 2;
// scaling animation X
DoubleAnimation scAnimX = new DoubleAnimation();
scAnimX.Duration = new Duration(TimeSpan.FromMilliseconds(500));
scAnimX.From = currentScaleFacX;
scAnimX.To = currentScaleFacX = scaleFac;
Storyboard.SetTargetName(scAnimX, canvasAll.Name);
Storyboard.SetTargetProperty(scAnimX, new PropertyPath("(0).(1)[0].(2)", Canvas.RenderTransformProperty, TransformGroup.ChildrenProperty, ScaleTransform.ScaleXProperty));
myStory.Children.Add(scAnimX);
// scaling animation Y
DoubleAnimation scAnimY = new DoubleAnimation();
scAnimY.Duration = new Duration(TimeSpan.FromMilliseconds(500));
scAnimY.From = currentScaleFacY;
scAnimY.To = currentScaleFacY = scaleFac;
Storyboard.SetTargetName(scAnimY, canvasAll.Name);
Storyboard.SetTargetProperty(scAnimY, new PropertyPath("(0).(1)[0].(2)", Canvas.RenderTransformProperty, TransformGroup.ChildrenProperty, ScaleTransform.ScaleYProperty));
myStory.Children.Add(scAnimY);
myStory.Begin(this);
}
private void _MouseRightButtonDown(object sender, MouseEventArgs e)
{
Storyboard myStory = new Storyboard();
scaleAll.CenterX = currentCx;
scaleAll.CenterY = currentCy;
// scaling animation X
DoubleAnimation scAnimX = new DoubleAnimation();
scAnimX.Duration = new Duration(TimeSpan.FromMilliseconds(500));
scAnimX.To = currentScaleFacX = currentScaleFacY = 1;
Storyboard.SetTargetName(scAnimX, canvasAll.Name);
Storyboard.SetTargetProperty(scAnimX, new PropertyPath("(0).(1)[0].(2)", Canvas.RenderTransformProperty, TransformGroup.ChildrenProperty, ScaleTransform.ScaleXProperty));
myStory.Children.Add(scAnimX);
// scaling animation Y
DoubleAnimation scAnimY = new DoubleAnimation();
scAnimY.Duration = new Duration(TimeSpan.FromMilliseconds(500));
scAnimY.To = currentScaleFacX = currentScaleFacY = 1;
Storyboard.SetTargetName(scAnimY, canvasAll.Name);
Storyboard.SetTargetProperty(scAnimY, new PropertyPath("(0).(1)[0].(2)", Canvas.RenderTransformProperty, TransformGroup.ChildrenProperty, ScaleTransform.ScaleYProperty));
myStory.Children.Add(scAnimY);
myStory.Begin(this);
}
}

Center and Scale Objects (Zooming & Panning)
Little John
Is there a possibility to transform a canvas (simultaneous scale &
translate) within one animation
BonnieFe
Hello Werner,
Thanks for your post. I have two guidelines that may help your scenario.
First of all, make sure when you target an animation that you define a base value. For example, in the sample above, you never specify the TranslateTransform (or its base X and Y properties) on the Canvas you are animating. This may work under some circumstances, but is not recommended.
Secondly, have you tried not specifying a From value in your animations Even for the first animation you run, you don't need a From value (the base value of the property is used). When you begin a second Storyboard, the default behavior is for the current value to be used as the "From" value.
About your second question, I do not know of any ways of animating scale and translate with a single animation (and still have it do something useful). The fact that you used a single storyboard for both is probably the best way of doing this!
Sirisha
the other thing you might want to do is add HandoffBehavior.Compose to the Begin() call of your Storyboard. the default behavior is to SnapshotAndReplace which would effectively stop other animations on that object/property.
myStory.Begin(this, HandoffBehavior.Compose);