最近一直在考虑如何设计系统的启动封面,关键在于封面上众多的按钮无法很好的分类组织起来。后来在魔兽中得到了启示。
其实最简约的设计最具持久美感,所以放弃了Panel、Tab容器的设计,而是参考War3的界面,按功能分成大类,当点击每个大类按钮时,对应的子功能按钮会像War3的侧边栏一样弹出,效果还是比较炫的。
具体效果见War3的侧边栏吧,简单描述就是左边的两排按钮会从上面弹下来,就像皮球落地一样。下面讲一下具体实现。
从初中物理知识中我们可以了解到这种运动属于阻尼运动,而皮球落地可以用取绝对值的阻尼运动公式来描述,程序中为了运动美观而没有取绝对值。
阻尼运动又分为欠阻尼、过阻尼及临界阻尼,概念就不多说了,有兴趣自己看,在后面的代码分析中将会看到按钮运动是欠阻尼。
阻尼运动公式:x(t) = A * exp(-1 * β * t) * cos(sprt(ω^2 - β^2 * t) + φ) , β是阻尼因子,ω是固有角频率。通过调整这两个参数的值即可获得我们预想的运动形式。
下面列举出代码部分,语言为C#。
double A_1 = 170 , A_2 = 170 , A_3 = 170 ;
double LocalY_1, LocalY_2, LocalY_3;
double w_1 = 5 , w_2 = 3 , w_3 = 1 ;
double B_1 = 0.4 , B_2 = 0.3 , B_3 = 0.5 ;
double t_1 = 0 , t_2 = 0 , t_3 = 0 ;
double LocalY_temp = 0 ;
while ( true )
{
LocalY_1 = A_1 * Math.Exp( - 1 * B_1 * t_1) * Math.Cos(Math.Sqrt(w_1 * t_1));
LocalY_2 = A_2 * Math.Exp( - 1 * B_2 * t_2) * Math.Cos(Math.Sqrt(w_2 * t_2));
LocalY_3 = A_3 * Math.Exp( - 1 * B_3 * t_3) * Math.Cos(Math.Sqrt(w_3 * t_3));
t_1 = t_1 + 0.5 ;
t_2 = t_1;
t_3 = t_1;
bttn_LIV.Location = new Point( 160 , fixY_1 - Convert.ToInt16(LocalY_1));
bttn_VBR.Location = new Point( 320 , fixY_1 - Convert.ToInt16(LocalY_1));
bttn_OSA.Location = new Point( 160 , fixY_2 - Convert.ToInt16(LocalY_2));
bttn_Sens.Location = new Point( 320 , fixY_2 - Convert.ToInt16(LocalY_2));
bttn_BR.Location = new Point( 160 , fixY_3 - Convert.ToInt16(LocalY_3));
bttn_Respon.Location = new Point( 320 , fixY_3 - Convert.ToInt16(LocalY_3));
Application.DoEvents();
if (LocalY_temp == LocalY_1)
break ;
else
LocalY_temp = LocalY_1;
}
So easy,呵呵,上述程序中的大循环即完成了预期的效果。fix_Y 1-3定义了三行按钮停止后的Location.Y值,A_1 ~ A-3定义了三行按钮开始运动时的Location.Y值,w_1 ~ w_3,B_1 ~ B_3分别是固有角频率和阻尼因子,这六个值是关键值,分别决定了三行按钮的下落运动轨迹。这六个值需要根据情况进行调整,是整个程序比较耗时的部分。
程序的整体思路是每循环一次,时间参数t_1~t_3增加,然后计算按钮的Location.Y值,改变按钮位置,直到两次循环按钮位置不再发生变化位 置。时间参数决定了运动的细腻程度,这个可以参考奈奎斯特定理,但由于CPU的运算周期一定,所以过小的时间步长会导致按钮运动过慢,这样又需要调整w和 β参数来改善运动速度,这三个变量互相影响,需要按实际情况进行调整。
另外在程序运行时更改控件位置需要开启双缓冲,在.NET中这步操作非常简单,在窗体构造函数中加入:
this .UpdateStyles();
关于双缓冲的内容就不介绍了。
如果各位同学还有什么更好的算法欢迎讨论。