经过前面一十二回的介绍,我们这个项目已经有了一些效果,但至今还没有一点动画元素,上一节最后,我想尝试一下动画效果,结果还失败了。不过也正是以此为契机,使我在动画方面有了一些实践,这一节我们试着做一些动画效果出来。
比如当点击基金名称后,以一个翻转效果切换到基金详情页面
呵呵,虽然比较丑,但我们关注的是实现,毕竟不是美工嘛。
按照我们一贯的风格,不去介绍长篇大论的理论与基础,我们还是以动手实践为主。
提到动画,自然就会想到Storyboard,没错,它是一切动画的基础,除非你自己写计时器去自己控制。那我们就先建一个Storyboard!在哪建呢?我们的动画过渡是要在行情与详情页面间做切换,自然动画就要建立在它们的父级,也就是“基金行情”项目的MainPage中了。(再强调一下,我们是以学习为主,并不是在做项目,因此我们这里没有用什么模式,只以简单实现为主)
看一下动画的实现原理,应该很自然就可以想到,用一个动画控制一个容器旋转,转到垂直角度后,把容器中的控件换成目标控件,再把容器用另一个动画还原回原来的状态。这里为什么要用两个动画来实现,是因为我们可以在第一个动画的结束事件里来做切换控件的动作,而除了Completed事件外,我还真没找到还有其它事件。
回顾一下我们的MainPage结构
SVMain就是我们的容器,它是一个ScrollViewer,因为不同的功能页面大小是不同的,所以使用ScrollViewer做为容器可以解决滚动问题,那么我们的动画就是设置它翻转就可以了。第一个动画Storyboard_GoToDetails
我们在0秒与1秒的位置插入关键帧,并设置1秒处的SVMain的旋转角度为-90度,如
接下来创建第二个动画Storyboard_GoToDetails_Second
效果与第一个相反,即在第0秒的位置,设置SVMain的旋转角度为-90度,在第1秒的位置还原
动画的代码全由Blend自动生成,如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<UserControl.Resources>
<Storyboard x:Name="Storyboard_GoToDetails">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="SVMain">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)" Storyboard.TargetName="SVMain">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="-90"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="Storyboard_GoToDetails_Second">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)" Storyboard.TargetName="SVMain">
<EasingDoubleKeyFrame KeyTime="0" Value="90"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
然后我们在第一个动画的Complete事件中切换控件,并开始第二个动画,如
void Storyboard_GoToDetails_Completed(object sender, EventArgs e)
{
FundDetails fd = new FundDetails();
SVMain.Content =fd;
this.Storyboard_GoToDetails_Second.Begin();
}
然后我们在行情控件里点击基金名称时通过委托或MainPage的引用启动第一个动画,我们的效果就实现了,很简单吧!
再看看我们第二个动画的效果,即在详情页面时通过点击导航上的行情按钮返回基金行情页面,如
一个类似3D翻转的效果,这个效果我们分析一下,其实也不很难,主要是控制好角度与位置,但显然的,一个容器满足不了要求了,因为我们会有两个控件同时存在的情况。那我们就要修改一下MainPage中的布局,如
具体实现的效果,网上有网友发布了一个封装好的类cShow3DPlane,可以拿来直接用,调用代码如
private cShow3DPlane c3d = new cShow3DPlane();
if (c3d.MoveOver())
{
c3d.SetInOutPlane(this.gridS1, this.gridS2, enumDirection.Right);
c3d.Begin();
}
这块有一点要注意一下,默认情况下,当一个动画结束后,它会保留它最终的状态,因此,当我们做切换的时候,要注意两个grid的状态,在我们这个实现中,为了保留第一个动作的效果(因为两个动画是不同的实现,第一个动画是用同一个容器实现的),我需要记录下当前是哪一个grid正在被显示,而且如果当前已经是在行情界面的话,再点击行情导航按钮应该就不需要再有动作了,最终代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private Grid currentGrid;//当前控件窗口,用以动画切换
private void LoadFundQuotes(Grid grids)
{//加载基金行情
FundQuotes fq = new FundQuotes();
fq.mp = this;
grids.Children.Clear();
grids.Children.Add(fq);
}
private void HyperlinkButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (currentGrid.Children.Count > 0 && currentGrid.Children[0] as FundQuotes == null)
{//如果当前显示模块不是基金行情
if (currentGrid == this.gridS1)
{
LoadFundQuotes(this.gridS2);
if (c3d.MoveOver())
{
c3d.SetInOutPlane(this.gridS2, this.gridS1, enumDirection.Right);
c3d.Begin();
currentGrid = this.gridS2;
}
}
else if (currentGrid == this.gridS2)
{
LoadFundQuotes(this.gridS1);
if (c3d.MoveOver())
{
c3d.SetInOutPlane(this.gridS1, this.gridS2, enumDirection.Right);
c3d.Begin();
currentGrid = this.gridS1;
}
}
}
}
因为布局发生了变化,因此第一个动画的实现也要做相应的修改,最终代码如
void Storyboard_GoToDetails_Completed(object sender, EventArgs e)
{
FundDetails fd = new FundDetails();
fd.currentFund = this.gridS1.Tag as WcfService.FundQuotes;
currentGrid.Children.Clear();
currentGrid.Children.Add(fd);
this.Storyboard_GoToDetails_Second.Begin();
}
fd.currentFund = this.gridS1.Tag as WcfService.FundQuotes;这一句我是用来向详情界面传参用的,与动画本身无关。