Skip to content

Commit

Permalink
页面旋转效果
Browse files Browse the repository at this point in the history
  • Loading branch information
zcbenz committed Mar 3, 2012
1 parent ca96302 commit 34680aa
Show file tree
Hide file tree
Showing 8 changed files with 783 additions and 5 deletions.
3 changes: 2 additions & 1 deletion App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ private void InitializePhoneApplication()

// Create the frame but don't set it as RootVisual yet; this allows the splash
// screen to remain active until the application is ready to render.
RootFrame = new TransitionFrame();
RootFrame = new Delay.HybridOrientationChangesFrame();
((Delay.HybridOrientationChangesFrame)RootFrame).Duration = TimeSpan.FromSeconds(0.8);
RootFrame.Navigated += CompleteInitializePhoneApplication;

// Handle navigation failures
Expand Down
2 changes: 1 addition & 1 deletion BoardPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="696" d:DesignWidth="480"
d:DataContext="{d:DesignData SampleData/CurrentBoardViewModelSampleData.xaml}"
shell:SystemTray.IsVisible="True"
Expand Down
330 changes: 330 additions & 0 deletions DynamicOrientationChanges/AnimateOrientationChangesFrame.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
// Copyright (C) Microsoft Corporation. All Rights Reserved.
// This code released under the terms of the Microsoft Public License
// (Ms-PL, http://opensource.org/licenses/ms-pl.html).

using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Microsoft.Phone.Controls;

namespace Delay
{
/// <summary>
/// PhoneApplicationFrame subclass that animates device orientation changes.
/// </summary>
public class AnimateOrientationChangesFrame : TransitionFrame
{
/// <summary>
/// Stores the Transform used to do the rotation.
/// </summary>
private readonly RotateTransform _rotateTransform = new RotateTransform();

/// <summary>
/// Stores the Transform used for centering the visuals.
/// </summary>
private readonly TranslateTransform _translateTransform = new TranslateTransform();

/// <summary>
/// Stores the Storyboard used to change the Progress value.
/// </summary>
private readonly Storyboard _progressStoryboard = new Storyboard();

/// <summary>
/// Stores the Animation used to change the Progress value.
/// </summary>
private readonly DoubleAnimation _progressAnimation = new DoubleAnimation();

/// <summary>
/// Stores the "from" state.
/// </summary>
private readonly OrientationState _from = new OrientationState();

/// <summary>
/// Stores the "to" state.
/// </summary>
private readonly OrientationState _to = new OrientationState();

/// <summary>
/// Stores a reference to the "ClientArea" template part.
/// </summary>
private UIElement _clientArea;

/// <summary>
/// Stores the last computed size from OnProgressChanged.
/// </summary>
private Size _lastSize;

/// <summary>
/// Stores a flag indicating whether the SizeChanged event has been handled yet.
/// </summary>
private bool _handledSizeChanged;

/// <summary>
/// Initializes a new instance of the AnimateOrientationChangesFrame class.
/// </summary>
public AnimateOrientationChangesFrame()
{
// Find existing "offset transform" and take it over (if possible) to support SIP raise/lower
var transformGroup = new TransformGroup();
var oldTransformGroup = RenderTransform as TransformGroup;
if ((null != oldTransformGroup) && (3 <= oldTransformGroup.Children.Count))
{
var offsetTransform = oldTransformGroup.Children[0] as TranslateTransform;
if (null != offsetTransform)
{
transformGroup.Children.Add(offsetTransform);
}
}
// Add custom transforms
transformGroup.Children.Add(_rotateTransform);
transformGroup.Children.Add(_translateTransform);
// Replace existing transform(s)
RenderTransform = transformGroup;

// Set up animation
_progressAnimation.From = 0;
_progressAnimation.To = 1;
Storyboard.SetTarget(_progressAnimation, this);
Storyboard.SetTargetProperty(_progressAnimation, new PropertyPath("Progress"));
_progressStoryboard.Children.Add(_progressAnimation);

// Initialize variables
EasingFunction = new QuarticEase(); // Initialized here to avoid a single shared instance

// Hook events
SizeChanged += new SizeChangedEventHandler(HandleSizeChanged);
OrientationChanged += new EventHandler<OrientationChangedEventArgs>(HandleOrientationChanged);
}

/// <summary>
/// Gets or sets a value indicating whether animation is enabled.
/// </summary>
public bool IsAnimationEnabled
{
get { return (bool)GetValue(IsAnimationEnabledProperty); }
set { SetValue(IsAnimationEnabledProperty, value); }
}
/// <summary>
/// Identifies the IsAnimationEnabled DependencyProperty.
/// </summary>
public static readonly DependencyProperty IsAnimationEnabledProperty =
DependencyProperty.Register("IsAnimationEnabled", typeof(bool), typeof(AnimateOrientationChangesFrame), new PropertyMetadata(true));

/// <summary>
/// Gets or sets a value indicating the duration of the orientation change animation.
/// </summary>
public TimeSpan Duration
{
get { return (TimeSpan)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
}
/// <summary>
/// Identifies the Duration DependencyProperty.
/// </summary>
public static readonly DependencyProperty DurationProperty =
DependencyProperty.Register("Duration", typeof(TimeSpan), typeof(AnimateOrientationChangesFrame), new PropertyMetadata(TimeSpan.FromSeconds(0.4)));

/// <summary>
/// Gets or sets a value indicating the IEasingFunction to use for the orientation change animation.
/// </summary>
public IEasingFunction EasingFunction
{
get { return (IEasingFunction)GetValue(EasingFunctionProperty); }
set { SetValue(EasingFunctionProperty, value); }
}
/// <summary>
/// Identifies the EasingFunction DependencyProperty.
/// </summary>
public static readonly DependencyProperty EasingFunctionProperty =
DependencyProperty.Register("EasingFunction", typeof(IEasingFunction), typeof(AnimateOrientationChangesFrame), new PropertyMetadata(null));

/// <summary>
/// Identifies the Progress DependencyProperty.
/// </summary>
private static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register("Progress", typeof(double), typeof(AnimateOrientationChangesFrame), new PropertyMetadata(0.0, OnProgressChanged));
/// <summary>
/// Handles changes to the Progress property.
/// </summary>
/// <param name="o">Event source.</param>
/// <param name="e">Event arguments.</param>
private static void OnProgressChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((AnimateOrientationChangesFrame)o).OnProgressChanged(/*(double)e.OldValue,*/ (double)e.NewValue);
}
/// <summary>
/// Handles changes to the Progress property.
/// </summary>
/// <param name="newValue">New value.</param>
private void OnProgressChanged(/*double oldValue,*/ double newValue)
{
// Update rotation
_rotateTransform.Angle = _from.Angle + (newValue * (_to.Angle - _from.Angle));
// Update translation (to center things)
var width = _from.Width + (newValue * (_to.Width - _from.Width));
var height = _from.Height + (newValue * (_to.Height - _from.Height));
var transformBounds = _rotateTransform.TransformBounds(new Rect(0, 0, width, height));
_translateTransform.X = ((ActualWidth - transformBounds.Width) / 2) - transformBounds.Left;
_translateTransform.Y = ((ActualHeight - transformBounds.Height) / 2) - transformBounds.Top;
// Invalidate only if size has changed
var size = new Size(Math.Round(width), Math.Round(height));
if (size != _lastSize)
{
_lastSize = size;
InvalidateMeasure();
}
}

/// <summary>
/// Called when the element's Template changes.
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Get the template part
_clientArea = base.GetTemplateChild("ClientArea") as UIElement;
}

/// <summary>
/// Handles the SizeChanged event.
/// </summary>
/// <param name="sender">Event source.</param>
/// <param name="e">Event arguments.</param>
private void HandleSizeChanged(object sender, SizeChangedEventArgs e)
{
// Capture new size
_from.Width = e.NewSize.Width;
_from.Height = e.NewSize.Height;
_lastSize = e.NewSize;

// Measure/Arrange can be called before the SizeChanged event fires
InvalidateMeasure();

// Record the method call and re-run any "early" orientation changes
_handledSizeChanged = true;
HandleOrientationChanged(null, new OrientationChangedEventArgs(Orientation));
}

/// <summary>
/// Handles the OrientationChanged event.
/// </summary>
/// <param name="sender">Event source.</param>
/// <param name="e">Event arguments.</param>
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "PageOrientation", Justification = "Spelled correctly.")]
private void HandleOrientationChanged(object sender, OrientationChangedEventArgs e)
{
if (!_handledSizeChanged)
{
// ActualWidth, ActualHeight, and _lastSize aren't valid yet
return;
}

// Capture current/before angle and size
_from.Angle = _rotateTransform.Angle;
_from.Width = _lastSize.Width;
_from.Height = _lastSize.Height;
_progressStoryboard.Stop();

// Determine new angle
switch (e.Orientation)
{
case PageOrientation.PortraitUp:
_to.Angle = 0;
break;
case PageOrientation.LandscapeLeft:
_to.Angle = 90;
break;
case PageOrientation.LandscapeRight:
_to.Angle = -90;
break;
case PageOrientation.PortraitDown:
_to.Angle = 180;
break;
default:
throw new NotSupportedException("Unknown PageOrientation value.");
}

// Determine new size
var actualWidth = ActualWidth;
var actualHeight = ActualHeight;
var toPortrait = (0 == (_to.Angle % 180));
_to.Width = toPortrait ? actualWidth : actualHeight;
_to.Height = toPortrait ? actualHeight : actualWidth;

if (IsAnimationEnabled && (null != sender))
{
// Animate the rotation
_progressAnimation.Duration = Duration;
_progressAnimation.EasingFunction = EasingFunction;
_progressStoryboard.Begin();
}
else
{
// Snap to the rotation (with guaranteed property change)
SetValue(ProgressProperty, 0.0);
SetValue(ProgressProperty, 1.0);
}
}

/// <summary>
/// Handles measuring the children of this element.
/// </summary>
/// <param name="availableSize">Available size.</param>
/// <returns>Desired size.</returns>
protected override Size MeasureOverride(Size availableSize)
{
if (null != _clientArea)
{
// Adjust measure size to transition size
var newValue = (double)GetValue(ProgressProperty);
var width = _from.Width + (newValue * (_to.Width - _from.Width));
var height = _from.Height + (newValue * (_to.Height - _from.Height));
_clientArea.Measure(new Size(width, height));
}
// Return default size
return availableSize;
}

/// <summary>
/// Handles arranging the children of this element.
/// </summary>
/// <param name="finalSize">Size to arrange to.</param>
/// <returns>Used size.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
if (null != _clientArea)
{
// Adjust arrange size to transition size
var newValue = (double)GetValue(ProgressProperty);
var width = _from.Width + (newValue * (_to.Width - _from.Width));
var height = _from.Height + (newValue * (_to.Height - _from.Height));
_clientArea.Arrange(new Rect(0, 0, width, height));
}
// Return default size
return finalSize;
}

/// <summary>
/// Stores state variables for orientation changes.
/// </summary>
private class OrientationState
{
/// <summary>
/// Gets or sets the Width.
/// </summary>
public double Width { get; set; }

/// <summary>
/// Gets or sets the Height.
/// </summary>
public double Height { get; set; }

/// <summary>
/// Gets or sets the Angle.
/// </summary>
public double Angle { get; set; }
}
}
}
Loading

0 comments on commit 34680aa

Please sign in to comment.