ScatterViewItem Flip Animation
-
Wednesday, January 20, 2010 7:56 PM
I am trying to animate my ScatterViewItem to resemble a "Flip" as in turning it over from the front side to the back side?
Any ideas on how I can do this., I am not sure how to apply the Transforms to the items.
Thanks
Answers
-
Thursday, January 21, 2010 8:49 PMModerator
rgarf, here is a sample repro that I was able to pull from the old Surface Commuinity site.
SurfaceWindow.XAML
<s:SurfaceWindow x:Class="SVIFlip.SurfaceWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="http://schemas.microsoft.com/surface/2008"
xmlns:local="clr-namespace:SVIFlip"
Title="SVIFlip"
Loaded="SurfaceWindow_Loaded"
Closed="SurfaceWindow_Closed"><s:SurfaceWindow.Resources>
<ImageBrush x:Key="WindowBackground" Stretch="None" Opacity="0.6" ImageSource="pack://application:,,,/Resources/WindowBackground.jpg"/>
<!-- Style the SVI to remove the background and border. This makes the flip look cleaner.
This does leave a shadow behind the SVI and if you want to remove that you will need to
retemplate the SVI to remove the chrome. -->
<Style BasedOn="{StaticResource {x:Type s:ScatterViewItem}}"
TargetType="{x:Type s:ScatterViewItem}"
x:Key="MySVI"><Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
</Style></s:SurfaceWindow.Resources>
<Grid Name="mainGrid" Background="{StaticResource WindowBackground}" >
<s:ScatterView>
<s:ScatterViewItem Style="{StaticResource MySVI}">
<local:FlipItem></local:FlipItem>
</s:ScatterViewItem>
</s:ScatterView>
</Grid>
</s:SurfaceWindow>
FlipItem.xaml
<s:SurfaceUserControl x:Class="SVIFlip.FlipItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="http://schemas.microsoft.com/surface/2008">
<UserControl.Resources>
<Storyboard x:Key="GoToBackStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumScaleTransform"
Storyboard.TargetProperty="ScaleX">
<SplineDoubleKeyFrame KeyTime="00:00:00.0" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="0.3"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.12" Value="0.6"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.15" Value="0.8"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.2" Value="1"/>
</DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumScaleTransform"
Storyboard.TargetProperty="ScaleY">
<SplineDoubleKeyFrame KeyTime="00:00:00.0" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="0.9"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.2" Value="1"/>
</DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumSkewTransform"
Storyboard.TargetProperty="AngleY">
<SplineDoubleKeyFrame KeyTime="00:00:00.06" Value="-10"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="-20"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.1" Value="20"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="GoToFrontStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumScaleTransform"
Storyboard.TargetProperty="ScaleX">
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="0.3"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.12" Value="0.6"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.15" Value="0.8"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="1"/>
</DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumScaleTransform"
Storyboard.TargetProperty="ScaleY">
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="0.9"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="1"/>
</DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumSkewTransform"
Storyboard.TargetProperty="AngleY">
<SplineDoubleKeyFrame KeyTime="00:00:00.06" Value="-10"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="-20"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.1" Value="20"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<UserControl.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="AlbumScaleTransform" ScaleX="1" ScaleY="1"/>
<SkewTransform x:Name="AlbumSkewTransform" AngleX="0" AngleY="0"/>
</TransformGroup>
</UserControl.RenderTransform><!-- Outer grid of our SVI display-->
<Grid Name="containerGrid">
<!-- Frontside of SVI-->
<Grid x:Name ="frontsideGrid" Background="Black" Width="100">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions><TextBlock TextAlignment="Right" Grid.Row="0">Front Side</TextBlock>
<s:SurfaceButton Grid.Row="1" Click="SwitchToBack">Go to Back</s:SurfaceButton>
</Grid>
<!-- Backside of SVI-->
<Grid x:Name="backsideGrid" Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions><TextBlock Grid.Row="0">Back Side</TextBlock>
<s:SurfaceButton Grid.Row="1" Click="SwitchToFront">Go to Front</s:SurfaceButton>
</Grid>
</Grid>
</s:SurfaceUserControl>
FlipItem.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
namespace SVIFlip
{
/// <summary>
/// Interaction logic for FlipItem.xaml
/// </summary>
public partial class FlipItem : SurfaceUserControl
{
public FlipItem()
{
InitializeComponent();
}
// Set the "back" to invisible.
// You need to remove the back side from the visual tree otherwise you will see a perf hit
// when the front side of the SVI is resized and WPF has to also calculate the layout for the back elements.
protected override void OnInitialized(EventArgs e)
{
backsideGrid.Visibility = Visibility.Hidden;
containerGrid.Children.Remove(backsideGrid);
base.OnInitialized(e);
}
/// <summary>
/// Flip album to back side.
/// </summary>
private void SwitchToBack(object sender, EventArgs e)
{
Storyboard goToBack = this.FindResource("GoToBackStoryboard") as Storyboard;
if (goToBack != null)
{
goToBack.Begin(this);
}
UpdateUIState(false);
}
/// <summary>
/// Filp album to front side.
/// </summary>
private void SwitchToFront(object sender, EventArgs e)
{
Storyboard goToFront = this.FindResource("GoToFrontStoryboard") as Storyboard;
if (goToFront != null)
{
goToFront.Begin(this);
}
UpdateUIState(true);
}/// <summary>
/// Update UI status on this control.
/// </summary>
/// <param name="isFront">if true, controls on front side are updated. if false, controls on back side are updated.</param>
private void UpdateUIState(bool isFront)
{
if (isFront)
{
frontsideGrid.Visibility = Visibility.Visible;
backsideGrid.Visibility = Visibility.Hidden;
// To improve performance, remove backsideGrid from Visual tree.
containerGrid.Children.Remove(backsideGrid);
}
else
{
// Add the backside back to the SVI
containerGrid.Children.Add(backsideGrid);
backsideGrid.Visibility = Visibility.Visible;
frontsideGrid.Visibility = Visibility.Hidden;
}
}
}
}
Josh Curry (jcurry) | Sr. Support Escalation Engineer | Microsoft Surface Support- Proposed As Answer by JCurryMicrosoft Employee, Moderator Thursday, January 21, 2010 8:49 PM
- Marked As Answer by Ben Tousey Wednesday, February 17, 2010 9:02 PM
All Replies
-
Thursday, January 21, 2010 8:49 PMModerator
rgarf, here is a sample repro that I was able to pull from the old Surface Commuinity site.
SurfaceWindow.XAML
<s:SurfaceWindow x:Class="SVIFlip.SurfaceWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="http://schemas.microsoft.com/surface/2008"
xmlns:local="clr-namespace:SVIFlip"
Title="SVIFlip"
Loaded="SurfaceWindow_Loaded"
Closed="SurfaceWindow_Closed"><s:SurfaceWindow.Resources>
<ImageBrush x:Key="WindowBackground" Stretch="None" Opacity="0.6" ImageSource="pack://application:,,,/Resources/WindowBackground.jpg"/>
<!-- Style the SVI to remove the background and border. This makes the flip look cleaner.
This does leave a shadow behind the SVI and if you want to remove that you will need to
retemplate the SVI to remove the chrome. -->
<Style BasedOn="{StaticResource {x:Type s:ScatterViewItem}}"
TargetType="{x:Type s:ScatterViewItem}"
x:Key="MySVI"><Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
</Style></s:SurfaceWindow.Resources>
<Grid Name="mainGrid" Background="{StaticResource WindowBackground}" >
<s:ScatterView>
<s:ScatterViewItem Style="{StaticResource MySVI}">
<local:FlipItem></local:FlipItem>
</s:ScatterViewItem>
</s:ScatterView>
</Grid>
</s:SurfaceWindow>
FlipItem.xaml
<s:SurfaceUserControl x:Class="SVIFlip.FlipItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="http://schemas.microsoft.com/surface/2008">
<UserControl.Resources>
<Storyboard x:Key="GoToBackStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumScaleTransform"
Storyboard.TargetProperty="ScaleX">
<SplineDoubleKeyFrame KeyTime="00:00:00.0" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="0.3"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.12" Value="0.6"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.15" Value="0.8"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.2" Value="1"/>
</DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumScaleTransform"
Storyboard.TargetProperty="ScaleY">
<SplineDoubleKeyFrame KeyTime="00:00:00.0" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="0.9"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.2" Value="1"/>
</DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumSkewTransform"
Storyboard.TargetProperty="AngleY">
<SplineDoubleKeyFrame KeyTime="00:00:00.06" Value="-10"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="-20"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.1" Value="20"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="GoToFrontStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumScaleTransform"
Storyboard.TargetProperty="ScaleX">
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="0.3"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.12" Value="0.6"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.15" Value="0.8"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="1"/>
</DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumScaleTransform"
Storyboard.TargetProperty="ScaleY">
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="0.9"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="1"/>
</DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AlbumSkewTransform"
Storyboard.TargetProperty="AngleY">
<SplineDoubleKeyFrame KeyTime="00:00:00.06" Value="-10"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.09" Value="-20"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.1" Value="20"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.18" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<UserControl.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="AlbumScaleTransform" ScaleX="1" ScaleY="1"/>
<SkewTransform x:Name="AlbumSkewTransform" AngleX="0" AngleY="0"/>
</TransformGroup>
</UserControl.RenderTransform><!-- Outer grid of our SVI display-->
<Grid Name="containerGrid">
<!-- Frontside of SVI-->
<Grid x:Name ="frontsideGrid" Background="Black" Width="100">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions><TextBlock TextAlignment="Right" Grid.Row="0">Front Side</TextBlock>
<s:SurfaceButton Grid.Row="1" Click="SwitchToBack">Go to Back</s:SurfaceButton>
</Grid>
<!-- Backside of SVI-->
<Grid x:Name="backsideGrid" Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions><TextBlock Grid.Row="0">Back Side</TextBlock>
<s:SurfaceButton Grid.Row="1" Click="SwitchToFront">Go to Front</s:SurfaceButton>
</Grid>
</Grid>
</s:SurfaceUserControl>
FlipItem.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
namespace SVIFlip
{
/// <summary>
/// Interaction logic for FlipItem.xaml
/// </summary>
public partial class FlipItem : SurfaceUserControl
{
public FlipItem()
{
InitializeComponent();
}
// Set the "back" to invisible.
// You need to remove the back side from the visual tree otherwise you will see a perf hit
// when the front side of the SVI is resized and WPF has to also calculate the layout for the back elements.
protected override void OnInitialized(EventArgs e)
{
backsideGrid.Visibility = Visibility.Hidden;
containerGrid.Children.Remove(backsideGrid);
base.OnInitialized(e);
}
/// <summary>
/// Flip album to back side.
/// </summary>
private void SwitchToBack(object sender, EventArgs e)
{
Storyboard goToBack = this.FindResource("GoToBackStoryboard") as Storyboard;
if (goToBack != null)
{
goToBack.Begin(this);
}
UpdateUIState(false);
}
/// <summary>
/// Filp album to front side.
/// </summary>
private void SwitchToFront(object sender, EventArgs e)
{
Storyboard goToFront = this.FindResource("GoToFrontStoryboard") as Storyboard;
if (goToFront != null)
{
goToFront.Begin(this);
}
UpdateUIState(true);
}/// <summary>
/// Update UI status on this control.
/// </summary>
/// <param name="isFront">if true, controls on front side are updated. if false, controls on back side are updated.</param>
private void UpdateUIState(bool isFront)
{
if (isFront)
{
frontsideGrid.Visibility = Visibility.Visible;
backsideGrid.Visibility = Visibility.Hidden;
// To improve performance, remove backsideGrid from Visual tree.
containerGrid.Children.Remove(backsideGrid);
}
else
{
// Add the backside back to the SVI
containerGrid.Children.Add(backsideGrid);
backsideGrid.Visibility = Visibility.Visible;
frontsideGrid.Visibility = Visibility.Hidden;
}
}
}
}
Josh Curry (jcurry) | Sr. Support Escalation Engineer | Microsoft Surface Support- Proposed As Answer by JCurryMicrosoft Employee, Moderator Thursday, January 21, 2010 8:49 PM
- Marked As Answer by Ben Tousey Wednesday, February 17, 2010 9:02 PM
-
Friday, January 22, 2010 11:18 PMIt seems that flipping is such a common requests, perhaps it should be in the next SDK release?? ;)
-
Monday, January 25, 2010 3:30 PMThanks alot, Can you please give me the link to the community site, since I am new to the Surface development, any useful sites would be helpful.
-
Monday, January 25, 2010 10:18 PMModeratorThe Surface Community site is now closed. That is where we discussed issues like this prior to migrating to using the MSDN and TechNet forums.
Josh Curry (jcurry) | Sr. Support Escalation Engineer | Microsoft Surface Support -
Wednesday, January 27, 2010 11:44 AMI have created some movement before and it looks realy great.
You could best use the lib found at CodeProject and combine it with ScatterViewItems http://www.codeproject.com/KB/WPF/ContentControl3D.aspx
I made a short video of a test i made with it on MS Surface, works and looks great (but this video only recorded at 30fps, so it looks bad)
http://www.youtube.com/watch?v=5I2o_MiJyy4 -
Saturday, June 19, 2010 7:18 PM
I've posted an example of how to do this with proper 3D transforms here.- Proposed As Answer by XMark Thursday, June 24, 2010 12:07 AM
-
Thursday, June 24, 2010 12:11 AM
Josh, thats a VERY impressive control. I would (and Im sure im not alone here) appreciate an overview in to how it all works, there are a lot of parts to that control and knowing more about it would be awesome.
Quesiton, have you noticed any performance issues when having a large amount of 2d controls on the Viewport2DVisual3D? I tried to make a 3d scene a little while ago with lots of 2d elements, and the performance really degraded quite quickly when having a lot of 2d elements...
Nice work though!
-
Thursday, June 24, 2010 5:37 PM
Hi Mark -- if you have specific questions I'd be happy to answer. Included in that solution is a PlaneTest project which shows the basics of the Plane control that's used to render the transform. That's included within the ScatterViewItem template to enable it to flip when the ScatterFlip.IsFlipped attached property is changed by triggering Flip/Unflip animations.
The app I'm using the control in shows a screenful of them at once and runs with reasonable performance. The way Plane is set up, it's not actually using Viewport2DVisual3D unless the control is actually rotated in some way, so there shouldn't be much of a perf hit unless you're rotating tons of them at once.
There are some perf improvements that could be made if you're willing to sacrifice interactivity and animation while rotated. There are other improvements that can be made using features in WPF 4, but Surface is still on 3.5 for now.
-
Thursday, June 24, 2010 11:00 PM
Hi Mark -- if you have specific questions I'd be happy to answer. Included in that solution is a PlaneTest project which shows the basics of the Plane control that's used to render the transform. That's included within the ScatterViewItem template to enable it to flip when the ScatterFlip.IsFlipped attached property is changed by triggering Flip/Unflip animations.
The app I'm using the control in shows a screenful of them at once and runs with reasonable performance. The way Plane is set up, it's not actually using Viewport2DVisual3D unless the control is actually rotated in some way, so there shouldn't be much of a perf hit unless you're rotating tons of them at once.
There are some perf improvements that could be made if you're willing to sacrifice interactivity and animation while rotated. There are other improvements that can be made using features in WPF 4, but Surface is still on 3.5 for now.
Hi Josh,What do you mean by, "not actually using Viewport2DVisual3D unless the control is actually rotated"? Do you mean rotated as in flipped, or rotated as is the RotateTransform?
Ill think of some questions Im sure of it when I take a deeper look into it :)
Cheers,
Mark
-
Friday, June 25, 2010 12:14 AM
To get technical: if RotationX % 180 == 0 && RotationY % 180 == 0 && RotationZ % 180 == 0, the content of the plane is shown outside of the 3D scene and just displayed inside of a regular Border. The scene still exists, but there's nothing in it. This wasn't done for performance reasons -- it just makes text look better.
In WPF4, this could be refactored to use ClearTypeHint to remove this complexity. You could also use BitmapCache to improve performance all over the place. As I work with the Surface Toolkit and WPF 4 more, I'll likely update the project to have a WPF4-specific version with these kinds of improvements.
I'm sure if you use a whole ton of these and spin them constantly, you'll see some perf issues, especially in 3.5, but it's worked ok for me. Watch the Surface blog, endquote.com, and stimulant.io in the coming weeks for a downloadable app which uses the control in a real-world scenario.

