转载地址: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)------------------------------------------------
<UserControl x:Class="_3dPlaneTest.MainPage"
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;
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();
}
}
}
}
以上是类和应用代码。