今天我们使用一个自定义控件实现3D变换(飞机飞出,树木飞入),先看一下最终效果:
DEMO演示地址:http://xingjunli.webs.com/view3dfly.html
1、新建template模版控件这时会自动生成一个“Themes/Generic.xaml”模版控件样式文件和继承自Control控件模版类如图:
2、在样式Generic.xaml文件中,我们 使用 <ControlTemplate TargetType="local:View3dTC"/>来定义作为控件模板应用的外观元素树(更多,或者比较复杂的应用请参考"http://msdn.microsoft.com/zh-cn/library/cc189093%28VS.95%29.aspx
")
使用"{TemplateBinding ControlProperty}"扩展标记,将控件模板中的属性值链接到在模板控件上公开的某个其他属性的上(如设置控件宽高、素材填充)完整定义如(这里面我们主要定义了控件外观):。
<Style TargetType="local:View3dTC">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:View3dTC">
<Grid x:Name="RootGrid" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Grid.RenderTransform>
<!--可以在模版中为控件定义状态及不同状态间的切换动画效果-->
<!--<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition To="MouseOver" GeneratedDuration="00:00:00.200"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation BeginTime="00:00:00" Duration="00:00:05" To="100" Storyboard.TargetName="faceRect" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" />
<DoubleAnimation BeginTime="00:00:00" Duration="00:00:05" To="100" Storyboard.TargetName="faceRect" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" />
<DoubleAnimation BeginTime="00:00:00" Duration="00:00:05" To="0.2" Storyboard.TargetName="faceRect" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" />
<DoubleAnimation BeginTime="00:00:00" Duration="00:00:05" To="0.2" Storyboard.TargetName="faceRect" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" />
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>-->
<Rectangle x:Name="faceRect" Margin="0,0,0,0" Fill="{TemplateBinding Face}">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
3、完成View3DTC控件代码:
a:在构造函数中我们使用DependencyProperty.Register 方法为控件注册依赖属性项,以方便我们在BLEND设计工具,和XAML中设置属性;详细参考"http://msdn.microsoft.com/zh-cn/library/ms597502%28VS.95%29.aspx
"
b:为组件注册TransformGroup属性
c:通过代码创建自定义动画
View3DTC完整代码如下,相关说明请留意其中的注释:
/// <summary>
/// 动态修改动画属性已使3D物体飞行的更随机一些
/// </summary>
/// <param name="stageW">主场景宽度</param>
/// <param name="stageH">主场景高度</param>
/// <param name="isOut">飞出 or 飞入</param>
/// <param name="dr"></param>
public void PlayAmination(Double stageW, Double stageH, bool isOut,Duration dr)
{
this.Visibility = Visibility.Visible;
if (storyBoardMove != null)
{
storyBoardMove.Duration = dr;
fromPoint = AnimationHelper.getAnimationPostionFrom(600, stageW, stageH, isOut);
toPoint = AnimationHelper.GetAnimationPostionTo(fromPoint, stageWidth, stageHeight, isOut);
for (int i = 0; i < storyBoardMove.Children.Count; i++)
{
da = storyBoardMove.Children[i] as DoubleAnimation;
if(da != null)
{
da.Duration = dr;
if (Storyboard.GetTargetProperty(da) != null)
{
switch (Storyboard.GetTargetProperty(da).Path)
{
case "(Canvas.Left)":
da.From = fromPoint.X;
da.To = toPoint.X;
break;
case "(Canvas.Top)":
da.From = fromPoint.Y;
da.To = toPoint.Y;
break;
default:
break;
}
}
}
}
storyBoardMove.Stop();
storyBoardMove.Begin();
}
else
{
storyBoardMove = CreateStoryBoard(stageW, stageH, isOut, dr);
storyBoardMove.Begin();
}
}
/// <summary>
/// 自定义动画
/// </summary>
/// <param name="stageWidth"></param>
/// <param name="stageHeight"></param>
/// <param name="isOut"></param>
/// <param name="dr"></param>
/// <returns></returns>
protected Storyboard CreateStoryBoard(Double stageWidth, Double stageHeight,bool isOut,Duration dr)
{
Storyboard sb = new Storyboard();
DoubleAnimation xA = new DoubleAnimation();
DoubleAnimation yA = new DoubleAnimation();
DoubleAnimation sX = new DoubleAnimation();
DoubleAnimation sY = new DoubleAnimation();
DoubleAnimation mA = new DoubleAnimation();
xA.Duration = dr;
yA.Duration = dr;
sX.Duration = dr;
sY.Duration = dr;
mA.Duration = dr;
if (isOut)
{
Point fP = AnimationHelper.GetBrowAreaPostion(stageWidth, stageHeight,200.0,100);
Point tP = AnimationHelper.GetAnimationPostionTo(fP, stageWidth, stageHeight, isOut);
xA.From = fP.X;
xA.To = tP.X;
yA.From = fP.Y;
yA.To = tP.Y;
sX.From = 1.1;
sX.To = 0.1;
sY.From = 1.1;
sY.To = 0.1;
mA.From = 1.1;
mA.To = 0.1;
}
else
{
Point fP = AnimationHelper.GetBrowAreaPostion(stageWidth, stageHeight, 200.0, 10);
Point tP = AnimationHelper.GetAnimationPostionTo(fP, stageWidth, stageHeight, isOut);
xA.From = fP.X;
xA.To = tP.X;
yA.From = fP.Y;
yA.To = tP.Y;
sX.From = 0.1;
sX.To = 1.1;
sY.From = 0.1;
sY.To = 1.1;
mA.From = 0.8;
mA.To = 1.1;
}
sb.Duration = dr;
sb.Children.Add(xA);
sb.Children.Add(yA);
sb.Children.Add(sX);
sb.Children.Add(sY);
sb.Children.Add(mA);
Storyboard.SetTarget(xA, this);
Storyboard.SetTarget(yA, this);
Storyboard.SetTarget(sX, (ScaleTransform)((TransformGroup)this.RenderTransform).Children[0]);
Storyboard.SetTarget(sY, (ScaleTransform)((TransformGroup)this.RenderTransform).Children[0]);
Storyboard.SetTarget(mA, this);
TransformGroup gp = this.RenderTransform as TransformGroup;
Storyboard.SetTargetProperty(xA, new PropertyPath("(Canvas.Left)"));
Storyboard.SetTargetProperty(yA, new PropertyPath("(Canvas.Top)"));
Storyboard.SetTargetProperty(sX, new PropertyPath(ScaleTransform.ScaleXProperty));
Storyboard.SetTargetProperty(sY, new PropertyPath(ScaleTransform.ScaleYProperty));
Storyboard.SetTargetProperty(mA, new PropertyPath("(UIElement.Opacity)"));
sb.Completed += new EventHandler(Fly_Completed);
return sb;
}
void Fly_Completed(object sender, EventArgs e)
{
//播放完成后继续播放动画,并随机设置位置信息
PlayAmination(this.stageWidth,this.stageHeight,this.isOut,this.Durction);
if (!isSetFace) //这里设置动画元件显示的外观
{
Rectangle rect = base.GetTemplateChild("faceRect") as Rectangle; //通过这个方法获取ControlTemplate外观元素
if (rect != null)
{
rect.Fill = Face;
}
isSetFace = true;
}
}
void View3dTC_MouseMove(object sender, MouseEventArgs e)
{
VisualStateManager.GoToState(this, "MouseOver", true);
}
#region DependencyProperty
public DependencyProperty FaceProperty;
public DependencyProperty stageWidthProperty; // 缩放X轴信息
public DependencyProperty stageHeightProperty; // 缩放Y轴信息
public DependencyProperty DurctionProperty; // 动画持续时间
public DependencyProperty isOutProperty; // 是否离开 true飞出,false飞来
/// <summary>
/// 显示的外观
/// </summary>
public Brush Face
{
get { return (ImageBrush)GetValue(FaceProperty); }
set {SetValue(FaceProperty,value);}
}
/// <summary>
/// 缩放X轴信息
/// </summary>
public Double stageWidth {
get { return (Double)GetValue(stageWidthProperty); }
set { SetValue(stageWidthProperty, value); }
}
/// <summary>
/// 缩放Y轴信息
/// </summary>
public Double stageHeight{
get { return (Double)GetValue(stageHeightProperty); }
set { SetValue(stageHeightProperty, value); }
}
/// <summary>
/// 缩放Z轴信息
/// </summary>
public bool isOut {
get { return (bool)GetValue(isOutProperty); }
set { SetValue(isOutProperty, value); }
}
/// <summary>
/// 动画持续时间
/// </summary>
public Duration Durction
{
get { return (Duration)GetValue(DurctionProperty); }
set { SetValue(DurctionProperty, value); }
}
#endregion
#region DependencyPropertyChangeEvent
//演示使用自定义属性,这里用来初始化动画演示
public void stageWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
View3dTC control = d as View3dTC;
control.PlayAmination(control.stageWidth,control.stageHeight,control.isOut,control.Durction);
}
}
#endregion
}
}
4、完成相关坐标的换算帮助类(其中应用了布朗随机方法以使随机分布均匀一些,可参考:http://blog.csdn.net/xingjunli/archive/2010/01/29/5270494.aspx
)
if (isOut)
{
result.X = fromPostion.X + (fromPostion.X - stageWidth * 0.5) * -0.9; //向中心点靠拢
result.Y = 100;
}
else
{
result.X = fromPostion.X + (fromPostion.X - stageWidth * 0.5) * 1.1; //向两边散开
result.Y = stageHeight - 100;
}
return result;
}
/// <summary>
/// 获取飞入或者飞出的坐标信息
/// </summary>
/// <param name="distWidth"></param>
/// <param name="stageWidth"></param>
/// <param name="stageHeight"></param>
/// <param name="isOut"></param>
/// <returns></returns>
public static Point getAnimationPostionFrom(double distWidth, double stageWidth, double stageHeight, bool isOut)
{
Point result = new Point(0, 0);
if (!isOut)
{
result.X = stageWidth * 0.5 + ((double)rand.Next(100)) * 0.01 * distWidth * 0.5 - distWidth * 0.22;
result.Y= 230;
}
else
{
result.X = stageWidth * 0.5 + ((double)rand.Next(100)) * 0.01 * distWidth - distWidth * 0.7;
result.Y = stageHeight;
}
return result;
}
}
}
5、最后就在主场景中使用我们上面创建的自定义控件了,这里面我通过后台代码控制上创建立组件,(自定义组件实用同SL的标准控件一样的),MainPage完整代码如:
View3dTC tc;
void timer_Tick(object sender, EventArgs e) //动态创建立3D变换控件
{
tc = new View3dTC();
if (controlCount % 2 == 0) //创建树控件
{
tc.Durction = new TimeSpan(0, 0, 8);
tc.Width = 26;
tc.Height = 150;
tc.stageHeight = 600;
tc.isOut = false;
tc.Face = treeBrush;
tc.stageWidth = 600;
}
else //创建飞机控件
{
tc.Durction = new TimeSpan(0, 0, rand.Next(6, 10));
tc.Width = 174;
tc.Height = 72;
tc.stageHeight = 600;
tc.isOut = true;
tc.Face = airplaneBrush;
tc.stageWidth = 600;
}
LayoutRoot.Children.Add(tc);
controlCount++;
if (controlCount >= 30)
{
timer.Stop();
timer.Tick -= timer_Tick; //完成后删除定时器注册
}
}
//动态修改远景抖动
void ManBgAnimation_Completed(object sender, EventArgs e)
{
(farBgAnimation.Children[0] as DoubleAnimation).To = rand.Next(-2, 2);
farBgAnimation.Begin();
}
void linkButtonblog_Click(object sender, RoutedEventArgs e)
{
System.Windows.Browser.HtmlPage.Window.Eval("window.open('http://blog.csdn.net/xingjunli');");
}
}
}
结束语:分享一个免费放SL应用的地方”http://www.webs.com/
“使用SL创建自定义组件,通过后台创建动画是不是很简单呢?如需要源码,请留Email.