(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Flipping panels

(124 votes)
Miroslav Miroslavov
>
Miroslav Miroslavov
Joined Nov 24, 2009
Articles:   8
Comments:   6
More Articles
3 comments   /   posted on May 05, 2010
Categories:   White Papers , General

This article is compatible with the latest version of Silverlight.

This is part 3 of the series “Silverlight in Action”

Here we’re sharing our experience from the amazing CompletIT web site.

In this ‘”how to” session, we’ll cover one of the most interesting and hardest to implement part of the site - How to flip the menu item and simultaneously show the actual page behind. The result of the effect should be like the page text is on the back of the menu.

Lets first see how it looks.

Flipping begin

Flipping_begin

Flipping back

Flipping

See it in action

  Get Microsoft Silverlight

Idea

The trick is in having two elements that live independent of each other – the menu and the actual page. We will flip the menu to the half, hide it at that point and animate the page from the mid point to the center of screen with normal size. So first we need to find the mid-point where we’ll change them.

Here is what happens in more detailed steps.

  1. Find the mid-point based on the centers of the menu item and the page
  2. Prepare the two elements
    1. Show the Page by changing it’s visibility
    2. Hide it with Opacity = 0 (those two steps are to prevent Layout pass when showing the page and hiding the menu – basically it shouldn’t happen layout pass thought the entire animation)
    3. Position the page at the mid-point and Rotate it on Y by –90°
    4. Scale it back to the size that the Menu item will have at the mid-point
  3. Begin flipping the Menu
    1. PlaneProjection.RotationY to 90°
    2. Translate the Menu item to the mid-point where will interchange with the page – TranslateTransform
  4. Hide the Menu item – Opacity = 0
  5. Show the Page – Opacity = 1
  6. Flip the Page from the mid point to the center of the screen
    1. Scale it to 1, to make it real size
    2. Translate it to it’s original position
    3. Rotate it on Y to 0

To implement this algorithm we’ll create Attached behavior for FrameworkElement with two attached properties:

  1. IsFlipping of type bool That will enable the flipping behavior for this element.
  2. Element of type FrameworkElement – That will give reference to the element at the back.

 

Implementation

 FrameworkElement back = GetElement(element);
 if (back == null)
     return;
  
 PlaneProjection cardProjection;
 element.EnsureProjection(out cardProjection);
  
 ScaleTransform cardScale;
 TranslateTransform cardTranslate;
 RotateTransform cardRotate;
 SkewTransform cardSkew;
  
 element.EnsureTransforms(out cardScale, out cardRotate, out cardSkew, out cardTranslate);
  
 ScaleTransform backScale;
 TranslateTransform backTranslate;
 RotateTransform backRotate;
 SkewTransform backSkew;
  
 back.EnsureTransforms(out backScale, out backRotate, out backSkew, out backTranslate);
  
 PlaneProjection backProjection;
 back.EnsureProjection(out backProjection);
 backProjection.RotationX = -90;
  
 back.Opacity = 0;
 back.Visibility = Visibility.Visible;
 root.UpdateLayout();
  
 //Position the two animations to meet at the mid. point.
 double w = (back.ActualWidth + element.ActualWidth) / 2;
 double h = (back.ActualHeight + element.ActualHeight) / 2;
  
 double scaleX = w / element.ActualWidth;
 double scaleY = h / element.ActualHeight;
  
 double backScaleX = w / back.ActualWidth;
 double backScaleY = h / back.ActualHeight;
  
 Point center = element.TransformToVisual(null).Transform(new Point(element.ActualWidth / 2, element.ActualHeight / 2));
   
 double x = root.ActualWidth / 2 - center.X;
 double y = root.ActualHeight / 2 - center.Y;
  
 backScale.ScaleX = backScaleX;
 backScale.ScaleY = backScaleY;
  
 backTranslate.X = -x / 2;
 backTranslate.Y = -y / 2;
  
 DoubleAnimation mainProjection = AnimationFactory.CreateProjectionXAnimation(cardProjection, 90, Seconds);
 DoubleAnimation scaleXAnimation = AnimationFactory.CreateScaleXAnimation(cardScale, scaleX, Seconds);
 DoubleAnimation scaleYAnimation = AnimationFactory.CreateScaleYAnimation(cardScale, scaleY, Seconds);
 DoubleAnimation translateXAnimation = AnimationFactory.CreateTranslateXAnimation(cardTranslate, cardTranslate.X + x / 2, Seconds);
 DoubleAnimation translateYAnimation = AnimationFactory.CreateTranslateYAnimation(cardTranslate, cardTranslate.Y + y / 2, Seconds);
  
 mainProjection.EasingFunction =
     translateXAnimation.EasingFunction =
     translateYAnimation.EasingFunction =
     scaleXAnimation.EasingFunction =
     scaleYAnimation.EasingFunction =
     new QuadraticEase
         {
             EasingMode = EasingMode.EaseIn
         };
  
 DoubleAnimation backProjectionAnimation = AnimationFactory.CreateProjectionXAnimation(backProjection, 0, Seconds);
 DoubleAnimation backScaleXAnimation = AnimationFactory.CreateScaleXAnimation(backScale, 1, Seconds);
 DoubleAnimation backScaleYAnimation = AnimationFactory.CreateScaleYAnimation(backScale, 1, Seconds);
 DoubleAnimation backTranslateXAnimation = AnimationFactory.CreateTranslateXAnimation(backTranslate, 0, Seconds);
 DoubleAnimation backTranslateYAnimation = AnimationFactory.CreateTranslateYAnimation(backTranslate, 0, Seconds);
  
 var backOpacityAnimation = new ObjectAnimationUsingKeyFrames();
 backOpacityAnimation.KeyFrames.Add(new DiscreteObjectKeyFrame
                                     {
                                         KeyTime = TimeSpan.FromSeconds(0),
                                         Value = 1
                                     });
 Storyboard.SetTarget(backOpacityAnimation, back);
 Storyboard.SetTargetProperty(backOpacityAnimation, new PropertyPath("Opacity"));
   
  
 backProjectionAnimation.BeginTime =
     backScaleXAnimation.BeginTime =
     backScaleYAnimation.BeginTime =
     backTranslateXAnimation.BeginTime =
     backTranslateYAnimation.BeginTime =
     backOpacityAnimation.BeginTime =
     TimeSpan.FromSeconds(Seconds);
  
 backProjectionAnimation.EasingFunction =
     backScaleXAnimation.EasingFunction =
     backScaleYAnimation.EasingFunction =
     backTranslateXAnimation.EasingFunction =
     backTranslateYAnimation.EasingFunction =
     new QuadraticEase
         {
             EasingMode = EasingMode.EaseOut
         };
  
 var flipping = new Storyboard();
  
 flipping.Children.Add(mainProjection);
 flipping.Children.Add(scaleXAnimation);
 flipping.Children.Add(scaleYAnimation);
 flipping.Children.Add(translateXAnimation);
 flipping.Children.Add(translateYAnimation);
   
 flipping.Children.Add(backOpacityAnimation);
 flipping.Children.Add(backProjectionAnimation);
 flipping.Children.Add(backScaleXAnimation);
 flipping.Children.Add(backScaleYAnimation);
 flipping.Children.Add(backTranslateXAnimation);
 flipping.Children.Add(backTranslateYAnimation);
   
 flipping.Completed += (s, e1) =>
 {
     element.Visibility = Visibility.Collapsed;
 };
 flipping.Begin();

Using the behavior is as easy as declaring two visuals, for example Grids, and wire them up – one to serve as front page and one as back.

How to use the behavior

 <Grid x:Name="front"
       Height="100"
      Width="100"
       FlippingCard:DraggingBehavior.IsDraggable="True"
       FlippingCard:FlippingBehavior.IsFlipping="True"
       FlippingCard:FlippingBehavior.Element="{Binding ElementName=back}">
  
     <ContentControl Style="{StaticResource faceControl}"
                    Background="Yellow" />
 </Grid>
  
 <Grid Margin="10"
       x:Name="back"
       Background="PowderBlue"
       MouseLeftButtonUp="BackMouseLeftButtonUp">
     <StackPanel HorizontalAlignment="Center"
                 VerticalAlignment="Center">
         <TextBlock Text="Back Page"
                    FontSize="48" />
         <TextBlock Text="Click to go back"
                    Foreground="Gray"
                    FontSize="18" />
     </StackPanel>
     <Grid.Effect>
         <DropShadowEffect BlurRadius="5"
                           Color="Red"
                           ShadowDepth="0"
                           Opacity=".7" />
     </Grid.Effect>
 </Grid>

Download Source Code.

Stay tuned for the next articles from this series coming up next week.


Subscribe

Comments

  • -_-

    RE: Flipping panels


    posted by Aaron on May 13, 2010 16:42
    Very nice, thanks!
  • -_-

    RE: Flipping panels


    posted by Peter Pan on Mar 30, 2011 07:46

    This is very nice, dear sir please provide vb code download, because i failed to convert this Public Shared ReadOnly ElementProperty As DependencyProperty = DependencyProperty.RegisterAttached("Element", GetType(FrameworkElement), GetType(FlippingBehavior), New PropertyMetadata(Nothing, OnElementChanged)), say that argument not specified for d,...

  • -_-

    RE: Flipping panels


    posted by Stevo on May 31, 2011 14:05

    Again Miroslav I am impressed! It looks tarrific!

    Greetings

    Windows 8

Add Comment

Login to comment:
  *      *       

From this series