现在的位置: 首页 > 综合 > 正文

阻尼运动算法在界面编程中的应用

2018年04月05日 ⁄ 综合 ⁄ 共 2994字 ⁄ 字号 评论关闭

最近一直在考虑如何设计系统的启动封面,关键在于封面上众多的按钮无法很好的分类组织起来。后来在魔兽中得到了启示。
        其实最简约的设计最具持久美感,所以放弃了Panel、Tab容器的设计,而是参考War3的界面,按功能分成大类,当点击每个大类按钮时,对应的子功能按钮会像War3的侧边栏一样弹出,效果还是比较炫的。
       
         具体效果见War3的侧边栏吧,简单描述就是左边的两排按钮会从上面弹下来,就像皮球落地一样。下面讲一下具体实现。
         从初中物理知识中我们可以了解到这种运动属于阻尼运动,而皮球落地可以用取绝对值的阻尼运动公式来描述,程序中为了运动美观而没有取绝对值。
         阻尼运动又分为欠阻尼、过阻尼及临界阻尼,概念就不多说了,有兴趣自己看,在后面的代码分析中将会看到按钮运动是欠阻尼。
        阻尼运动公式:x(t) = A * exp(-1 * β * t) * cos(sprt(ω^2 - β^2 * t) + φ) ,
β是阻尼因子,ω是固有角频率。通过调整这两个参数的值即可获得我们预想的运动形式。
        下面列举出代码部分,语言为C#。
        

        
int
            fixY_1 
=
 
224
,                fixY_2 
=
 
280
,                fixY_3 
=
 
336
;

        

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
.SetStyle(ControlStyles.UserPaint 
|
 ControlStyles.DoubleBuffer 
|
 ControlStyles.AllPaintingInWmPaint  
|
 ControlStyles.ResizeRedraw 
|
 ControlStyles.SupportsTransparentBackColor, 
true
);

            

this
.UpdateStyles();


         关于双缓冲的内容就不介绍了。
         如果各位同学还有什么更好的算法欢迎讨论。

抱歉!评论已关闭.