转载地址:http://www.cnblogs.com/fanxiaokaitp/archive/2010/05/21/1740532.html
最近在做一个SilverLight项目,两个屏幕面板切换(比如从一个Grid切换到另一个Grid)时需要用到3D旋转的方式,部分文字、按钮切换时也要用到3D旋转的方式,刚开始看到这样需求的时候,自然而然的想到了用storyboard来做动画,于是开始动起手来画,研究了一阵3D旋转的方式,画出了第一个面板的3D左切换方式,但随之而来的问题让人头疼,不光有右、上、下的不同甚至随机的切换方式,还有不同控件、动画速度等需要控制,用动画的方式做出来似乎程序的伸缩很小,也不是太方便,于是有了写一个实现这样功能的类来用的想法,日后如果碰到需要这类功能的需要还可以重复使用。
用了一天来写代码并测试,感觉还算可以,写代码时用了几种Timer来尝试控制动画,包括使用Thread,发现还是用storyboard来做Timer比较合适,没有过多的限制和线程外控件等问题,所以类中的动画主要用了两个storyboard来做Timer,分别控控制一进一出,实现动画则用PlaneProjection类来控制。3D旋转的主要实现方法是对PlaneProjection类的LocalOffsetZ和GlobalOffsetZ属性值进行正确的设置,左右旋转时LocalOffsetZ设置为控件的Widh/2,上下旋转则为Height/2,GlobalOffsetZ设置为相应的负值,也就是GlobalOffsetZ=-LocalOffsetZ,如果不写这句,控件会被拉伸大一截,要加上这一句花了我好长时间在HELP看了老半天,其次就是RotationX和RotationY的角度设置问题,弄清楚后就基本可以写完成了。
找到了个可以上传XAP在线演示的地方,大家可以先看看在线演示,地址: http://www.bbniu.com/matrix/ShowApplication.aspx?id=122
下面是类代码,大家可以把类引用到工程里或者是编译成DLL引用,后面还写了一个基本的应用例子:
应用图例:
-----------------类代码(c3dplane.cs)------------------------------------------------
* 大家共同交流,共同进步
* QQ:16277488
* MAIL:fanxiaokaitp@163.com
*
* */
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace c3dplane
{
public enum enumDirection
{
Up, Down, Left, Right
};
public class cShow3DPlane
{
public cShow3DPlane()
{
m_IsMoveOverIn = true ;
m_IsMoveOverOut = true ;
m_MoveSpeed = 2 ;
sbTimeIn = new Storyboard();
sbTimeOut = new Storyboard();
SetTime( 10 , 10 );
sbTimeIn.Completed += new EventHandler(sbTimeIn_Completed);
sbTimeOut.Completed += new EventHandler(sbTimeOut_Completed);
}
private int m_TimeSpeed1, m_TimeSpeed2,m_MoveSpeed,m_PlaneSpeed,m_InEnd,m_OutEnd;
private double m_InLocalZ,m_OutLocalZ;
private PlaneProjection m_pInPlane,m_pOutPlane;
private Storyboard sbTimeIn, sbTimeOut;
private enumDirection m_Direction;
private bool m_IsMoveOverIn,m_IsMoveOverOut;
// 根据方向初始化两个planeProjection的相关值
private void InitPlaneData()
{
m_pInPlane.RotationX = 0 ;
m_pInPlane.RotationY = 0 ;
m_pInPlane.RotationZ = 0 ;
m_pInPlane.CenterOfRotationX = 0.5 ;
m_pInPlane.CenterOfRotationY = 0.5 ;
m_pInPlane.CenterOfRotationZ = 0 ;
m_pOutPlane.RotationX = 0 ;
m_pOutPlane.RotationY = 0 ;
m_pOutPlane.RotationZ = 0 ;
m_pOutPlane.CenterOfRotationX = 0.5 ;
m_pOutPlane.CenterOfRotationY = 0.5 ;
m_pOutPlane.CenterOfRotationZ = 0 ;
m_pOutPlane.LocalOffsetZ = m_OutLocalZ;
m_pOutPlane.GlobalOffsetZ = - m_OutLocalZ;
m_pInPlane.LocalOffsetZ = m_InLocalZ;
m_pInPlane.GlobalOffsetZ = - m_InLocalZ;
switch (m_Direction)
{
case enumDirection.Up:
m_pInPlane.RotationX = - 90 ;
m_InEnd = 0 ;
m_pOutPlane.RotationX = 0 ;
m_OutEnd = 90 ;
m_PlaneSpeed = m_MoveSpeed;
break ;
case enumDirection.Down:
m_pInPlane.RotationX = 90 ;
m_InEnd = 0 ;
m_pOutPlane.RotationX = 0 ;
m_OutEnd = - 90 ;
m_PlaneSpeed = - m_MoveSpeed;
break ;
case enumDirection.Left:
m_pInPlane.RotationY = 90 ;
m_InEnd = 0 ;
m_pOutPlane.RotationY = 0 ;
m_OutEnd = - 90 ;
m_PlaneSpeed = - m_MoveSpeed;
break ;
case enumDirection.Right:
m_pInPlane.RotationY = - 90 ;
m_InEnd = 0 ;
m_pOutPlane.RotationY = 0 ;
m_OutEnd = 90 ;
m_PlaneSpeed = m_MoveSpeed;
break ;
}
}
public void Begin()
{
m_IsMoveOverIn = false ;
m_IsMoveOverOut = false ;
sbTimeIn.Begin();
sbTimeOut.Begin();
}
// 设置进入和离开对象
public void SetInOutPlane(Grid gridIn, Grid gridOut, enumDirection eDirection)
{
m_pInPlane = new PlaneProjection();
m_pOutPlane = new PlaneProjection();
gridIn.Projection = m_pInPlane;
gridOut.Projection = m_pOutPlane;
if (eDirection == enumDirection.Left || eDirection == enumDirection.Right)
{
m_InLocalZ = gridIn.ActualWidth / 2 ;
m_OutLocalZ = gridOut.ActualWidth / 2 ;
}
else
{
m_InLocalZ = gridIn.ActualHeight / 2 ;
m_OutLocalZ = gridOut.ActualHeight / 2 ;
}
m_Direction = eDirection;
InitPlaneData();
}
// 重载
public void SetInOutPlane(Control controlIn, Control controlOut, enumDirection eDirection)
{
m_pInPlane = new PlaneProjection();
m_pOutPlane = new PlaneProjection();
controlIn.Projection = m_pInPlane;
controlOut.Projection = m_pOutPlane;
if (eDirection == enumDirection.Left || eDirection == enumDirection.Right)
{
m_InLocalZ = controlIn.ActualWidth / 2 ;
m_OutLocalZ = controlOut.ActualWidth / 2 ;
}
else
{
m_InLocalZ = controlIn.ActualHeight / 2 ;
m_OutLocalZ = controlOut.ActualHeight / 2 ;
}
m_Direction = eDirection;
InitPlaneData();
}
public bool MoveOver()
{
if ( ! m_IsMoveOverIn || ! m_IsMoveOverOut)
return false ;
else
return true ;
}
// 设置进入和离开动画的速度
public void SetTime( int timeSpeed1, int timeSpeed2)
{
m_TimeSpeed1 = timeSpeed1;
m_TimeSpeed2 = timeSpeed2;
sbTimeIn.Duration = new Duration(TimeSpan.FromMilliseconds(m_TimeSpeed1));
sbTimeOut.Duration = new Duration(TimeSpan.FromMilliseconds(m_TimeSpeed2));
}
// 离开对象的动画
void sbTimeOut_Completed( object sender, EventArgs e)
{
// throw new NotImplementedException();
if (m_Direction == enumDirection.Left || m_Direction == enumDirection.Right)
{
m_pOutPlane.RotationY += m_PlaneSpeed;
if (m_pOutPlane.RotationY == m_OutEnd) m_IsMoveOverOut = true ;
}
if (m_Direction == enumDirection.Up || m_Direction == enumDirection.Down)
{
m_pOutPlane.RotationX += m_PlaneSpeed;
if (m_pOutPlane.RotationX == m_OutEnd) m_IsMoveOverOut = true ;
}
if ( ! m_IsMoveOverOut)
sbTimeOut.Begin();
else
{
m_pOutPlane.LocalOffsetZ = 0 ;
m_pOutPlane.GlobalOffsetZ = 0 ;
}
}
// 进入对象的动画
void sbTimeIn_Completed( object sender, EventArgs e)
{
// throw new NotImplementedException();
if (m_Direction == enumDirection.Left || m_Direction == enumDirection.Right)
{
m_pInPlane.RotationY += m_PlaneSpeed;
if (m_pInPlane.RotationY == m_InEnd) m_IsMoveOverIn = true ;
}
if (m_Direction == enumDirection.Up || m_Direction == enumDirection.Down)
{
m_pInPlane.RotationX += m_PlaneSpeed;
if (m_pInPlane.RotationX == m_InEnd) m_IsMoveOverIn = true ;
}
if ( ! m_IsMoveOverIn)
sbTimeIn.Begin();
else
{
m_pInPlane.LocalOffsetZ = 0 ;
m_pInPlane.GlobalOffsetZ = 0 ;
}
}
}
}
下面是应用例子:
-----------------(MainPage.xaml)------------------------------------------------
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml "
xmlns:d = " http://schemas.microsoft.com/expression/blend/2008 "
xmlns:mc = " http://schemas.openxmlformats.org/markup-compatibility/2006 "
mc:Ignorable = " d "
d:DesignHeight = " 480 " d:DesignWidth = " 640 " >
< Grid x:Name = " LayoutRoot " Background = " White " ShowGridLines = " True " >
< Grid.RowDefinitions >
< RowDefinition ></ RowDefinition >
< RowDefinition ></ RowDefinition >
</ Grid.RowDefinitions >
< Grid.ColumnDefinitions >
< ColumnDefinition ></ ColumnDefinition >
< ColumnDefinition ></ ColumnDefinition >
</ Grid.ColumnDefinitions >
< Grid Grid.Row = " 0 " Grid.Column = " 0 " >
< Button x:Name = " btnUp " Content = " 上 " VerticalAlignment = " Top " HorizontalAlignment = " Center " Width = " 200 " Margin = " 20 " Click = " btnUp_Click " ></ Button >
< Button x:Name = " btnDown " Content = " 下 " VerticalAlignment = " Bottom " HorizontalAlignment = " Center " Width = " 200 " Margin = " 20 " Click = " btnDown_Click " ></ Button >
< Button x:Name = " btnLeft " Content = " 左 " VerticalAlignment = " Center " HorizontalAlignment = " Left " Height = " 200 " Margin = " 20 " Click = " btnLeft_Click " ></ Button >
< Button x:Name = " btnRight " Content = " 右 " VerticalAlignment = " Center " HorizontalAlignment = " Right " Height = " 200 " Margin = " 20 " Click = " btnRight_Click " ></ Button >
< Grid x:Name = " gridUp " Background = " Red " Width = " 200 " Height = " 100 " >
< TextBlock Text = " 这是上喔 " Foreground = " White " VerticalAlignment = " Top " HorizontalAlignment = " Center " />
</ Grid >
< Grid x:Name = " gridDown " Background = " Green " Width = " 200 " Height = " 100 " >
< TextBlock Text = " 这是下喔 " Foreground = " White " VerticalAlignment = " Bottom " HorizontalAlignment = " Center " />
</ Grid >
< Grid x:Name = " gridLeft " Background = " Blue " Width = " 200 " Height = " 100 " >
< TextBlock Text = " 这是左喔 " Foreground = " White " VerticalAlignment = " Center " HorizontalAlignment = " Left " />
</ Grid >
< Grid x:Name = " gridRight " Background = " Cornsilk " Width = " 200 " Height = " 100 " >
< TextBlock Text = " 这是右喔 " Foreground = " Red " VerticalAlignment = " Center " HorizontalAlignment = " Right " />
</ Grid >
</ Grid >
< Grid Grid.Row = " 0 " Grid.Column = " 1 " >
< Grid.RowDefinitions >
< RowDefinition ></ RowDefinition >
< RowDefinition ></ RowDefinition >
</ Grid.RowDefinitions >
< Grid.ColumnDefinitions >
< ColumnDefinition ></ ColumnDefinition >
</ Grid.ColumnDefinitions >
< Button x:Name = " btnTest1 " Content = " QQ:16277488,返回了 " Width = " 220 " Height = " 50 " HorizontalAlignment = " Center " VerticalAlignment = " Center " Click = " btnTest1_Click " ></ Button >
< Button x:Name = " btnTest2 " Content = " 这是测试控件旋转 " Width = " 220 " Height = " 50 " HorizontalAlignment = " Center " VerticalAlignment = " Center " Click = " btnTest2_Click " ></ Button >
< Button x:Name = " btnTest3 " Content = " mail:fanxiaokaitp@163.com,返回了 " Grid.Row = " 1 " Width = " 220 " Height = " 50 " HorizontalAlignment = " Center " VerticalAlignment = " Center " Click = " btnTest3_Click " ></ Button >
< Button x:Name = " btnTest4 " Content = " 这也是测试控件旋转 " Grid.Row = " 1 " Width = " 220 " Height = " 50 " HorizontalAlignment = " Center " VerticalAlignment = " Center " Click = " btnTest4_Click " ></ Button >
</ Grid >
</ Grid >
</ UserControl >
-----------------(MainPage.xaml.cs)------------------------------------------------
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using c3dplane;
namespace _3dPlaneTest
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
PlaneProjection pp = new PlaneProjection();
pp.RotationY = 90 ;
gridUp.Projection = pp;
gridDown.Projection = pp;
gridLeft.Projection = pp;
gridCur = gridRight;
}
private cShow3DPlane c3d = new cShow3DPlane();
private Grid gridCur = new Grid();
private void btnUp_Click( object sender, RoutedEventArgs e)
{
if (gridCur == gridUp ) { MessageBox.Show( " 现在已经是 上 了 " ); return ; }
if (c3d.MoveOver())
{
c3d.SetInOutPlane(gridUp, gridCur, enumDirection.Up);
gridCur = gridUp;
c3d.Begin();
}
}
private void btnRight_Click( object sender, RoutedEventArgs e)
{
if (gridCur == gridRight) { MessageBox.Show( " 现在已经是 右 了 " ); return ; }
if (c3d.MoveOver())
{
c3d.SetInOutPlane(gridRight, gridCur, enumDirection.Right);
gridCur = gridRight;
c3d.Begin();
}
}
private void btnLeft_Click( object sender, RoutedEventArgs e)
{
if (gridCur == gridLeft) { MessageBox.Show( " 现在已经是 左 了 " ); return ; }
if (c3d.MoveOver())
{
c3d.SetInOutPlane(gridLeft, gridCur, enumDirection.Left);
gridCur = gridLeft;
c3d.Begin();
}
}
private void btnDown_Click( object sender, RoutedEventArgs e)
{
if (gridCur == gridDown) { MessageBox.Show( " 现在已经是 下 了 " ); return ; }
if (c3d.MoveOver())
{
c3d.SetInOutPlane(gridDown, gridCur, enumDirection.Down);
gridCur = gridDown;
c3d.Begin();
}
}
private void btnTest2_Click( object sender, RoutedEventArgs e)
{
if (c3d.MoveOver())
{
c3d.SetInOutPlane(btnTest1, btnTest2, enumDirection.Left);
c3d.Begin();
}
}
private void btnTest1_Click( object sender, RoutedEventArgs e)
{
if (c3d.MoveOver())
{
c3d.SetInOutPlane(btnTest2, btnTest1, enumDirection.Right);
c3d.Begin();
}
}
private void btnTest3_Click( object sender, RoutedEventArgs e)
{
if (c3d.MoveOver())
{
c3d.SetInOutPlane(btnTest4, btnTest3, enumDirection.Up);
c3d.Begin();
}
}
private void btnTest4_Click( object sender, RoutedEventArgs e)
{
if (c3d.MoveOver())
{
c3d.SetInOutPlane(btnTest3, btnTest4, enumDirection.Down);
c3d.Begin();
}
}
}
}
以上是类和应用代码。