我们此次将要制作模拟物理中的弹性现象的交互动画,我们让一个小球向鼠标点击位置移动,这个移动的轨迹不是简单的位移,而是根据胡克定律计算得出的。
胡克定律:F=-kd
F代表弹性力的大小
d是拉伸的位移
k为弹性系数
在我们的动画中还将加入摩擦力,否则物体就如在真空中运动,永远不会停止。
需声明的是,以上知识并不属于我,也不是来自课本,要知道我的初中物理及格次数并不多,这些是来自于奇才Robert Penner的著作,而以下示例也是其书中Flash示例的翻版。
下面开始我们的示例:
首先我在Expression Design中设计了一个球的外观以及一个渐变的背景:
然后将它们导出为WPF资源字典,在Visual Studio中新建一个项目,引用上述资源做Ellipse元素和Window元素的背景,创建如下的界面:
需注意的是,要将两个Slider的Maximum属性设为1、LargeChange属性设为0.1,因为我们的两个属性取值范围都是在0-1之间。
在后台代码中将窗体的DataContext属性设为其自身,即“this”。
然后实现INotifyPropertyChanged接口,并设置4个用于前台绑定的4个属性:X、Y、弹性系数、流体摩擦力
之后将Ellipse的Canvas.Top及Canvas.Left属性分别绑定到Y和X,两个Slider的Value属性分别绑定到弹性系数和流体摩擦力。
之后为Window增加ContentRendered事件处理,在此将初始化一个计时器,以此来控制动画的每个帧:
DispatcherTimer 帧计时器;
private void Window_ContentRendered(object sender, EventArgs e)
{
帧计时器 = new DispatcherTimer();
帧计时器.Tick += new EventHandler(帧计时器_Tick);
帧计时器.Interval = TimeSpan.FromMilliseconds(10);
帧计时器.Start();
}
double x = 0;
double y = 0;
void 帧计时器_Tick(object sender, EventArgs e)
{
var X轴弹力 = (X - 目标位置.X) * -弹性系数;
x += X轴弹力;
x *= 1-流体摩擦力;
X += x;
var Y轴弹力 = (Y - 目标位置.Y) * -弹性系数;
y += Y轴弹力;
y *= 1 - 流体摩擦力;
Y += y;
}
计时器每次触发时,都将进行位置计算,并调整X、Y的值。
其中的目标位置是在鼠标单击时指定的,为此还要为Window增加PreviewMouseLeftButtonDown事件处理:
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var p = e.GetPosition(this);
目标位置 = new Point(p.X - Ball.ActualWidth / 2, p.Y - Ball.ActualHeight / 2);
}
在此事件处理中,将目标位置设为鼠标当前坐标减去Ellipse元素的实际尺寸的一半,这样在移动完成后,球的中心点就位于鼠标的点击处了。
PS:将PreviewMouseLeftButtonDown事件改为MouseLeftButtonDown也是可行的,但是窗体内的控件如果截获并处理了点击事件,那么将不会引发球体移动。
现在编译和运行后,显示的程序界面效果如下:
通过点击可以使球移动,调整两个参数可以看到不同的移动效果。
你可以通过下面这个视频大略了解此示例的效果,但它与实际的流畅度有些差距,程序中的帧频应当是每秒100帧的,而视频只有每秒25帧。
源视频下载(清晰度高一些):http://www.box.net/shared/fjv5kucgns
本文章的PDF文档下载:http://www.box.net/shared/rlqoqf5lkp