具有3D旋转效果的图片组的一种实现

       前段时间看到QQ群里面有朋友问一组图片的3D旋转效果怎么做,虽然之前看到过flash版本的这种效果,但是自己却也没有做过这种效果。于是,就想来练习练习,最终的效果是这样的:

 

 

其实,这个东西就是要利用椭圆的标准方程,然后,你通过一个dispatchertimer 每隔一段时间触发一次动作,使得里面的图片沿着椭圆的轨迹改变坐标点,另外可以再改变图片的大小和透明度,使得效果看起来更好点。

 

关于椭圆及其标准方程:

 

      

 

还有一个需要用到的椭圆的性质是,知道椭圆的两个半轴,根据角度求得在椭圆轨迹上的坐标点:

也就是椭圆的参数方程:

 

其中的角度是离心角:

 

 

具备了这些几何知识,接下来的应该很简单了,无非就是利用silverlight中的属性,变幻效果等等,使得每隔一定时间,让你的图片在如上图那样的椭圆轨迹上运动。好了,分析完毕,开始写代码:

首先,在xaml文件中如下写:

 

 <Canvas x:Name="MainCanvas" Background="Black">

    
</Canvas>

 

 

是不是很简单,因为这次把大多数的操作,包括图片的加载都放到隐藏代码中了,这里并没有鄙视xaml的意思,其实我很喜欢xaml声明式的代码,但是标记代码和后台的逻辑代码都是必不可少的,这也是silverlight和WPF的一大特色。只是,这次用到的逻辑比较多,在xmal中去做就不太合适。好了,接下来,看下代码隐藏文件中是怎么回事:

首先在MainPage控件类中定义这么些私有变量:

 

 private double _speed;                                                        //图片移动速度
        private List<double> _angles;                                                //离心角角度集合
        private const double _MAX_SPEED = 0.009;                                      //定义的最大速度值
        private DispatcherTimer _disp;                                                   //定时器
        private List<ScaleTransform> scaleTransforms = new List<ScaleTransform>();        //缩放变换集合

 

 

然后,我们需要一个初始化的函数,包括加载图片啊,设置图片大小,初始离心角角度集合的初始化

 

代码
  public void InitContainer()
        {
            _angles 
= new List<double>();
            _speed 
= _MAX_SPEED;
            _disp 
= new DispatcherTimer();
            _disp.Interval 
= TimeSpan.FromMilliseconds(25);                              //每隔25毫秒,图片移动一次
            _disp.Tick += new EventHandler(_disp_Tick);
           
            
for (var i = 0; i < 6; i++)
            {
                Canvas ca 
= new Canvas();
                BitmapImage bit
=new BitmapImage(new Uri(@"Images/"+(i+1)+".jpg",UriKind.Relative));
                Image img
=new Image();
                img.Width 
= 200;
                img.Height 
= 250;
                img.Source
=bit;
                ca.Width 
= 200;
                ca.Height 
= 250;
                
//ca.Background = new SolidColorBrush(Colors.Red);
                ca.Children.Add(img);
                _angles.Add ( (Math.PI 
* 2 / 6* i);
                ScaleTransform sc 
= new ScaleTransform();
                scaleTransforms.Add(sc);
                sc.ScaleX 
= 1;
                sc.ScaleY 
= 1;
                sc.CenterX 
= 100;
                sc.CenterY 
= 125;
                ca.RenderTransform 
= sc;
                MainCanvas.Children.Add(ca);
            }
            _disp.Start();
        }

 

 

然后再写一个移动图片的函数,主要功能:根据离心角的角度通过椭圆参数方程计算出此刻图片位于椭圆轨迹上的坐标点,然后再进行图片缩放,透明度改变等效果

 

 public void SetMove()
        {
            
double RadiusX = 300;                                       //设定椭圆的长半轴
            double RadiusY = 80;                                        //设定椭圆的短半轴
            double CenterX = 550;                                        //设定图片组的中心
            double CenterY = 220;                                        //设定图片组的中心
            Canvas aCanvas;
            
for (int i = 0; i < 6; i++)
            {
               aCanvas 
= MainCanvas.Children[i] as Canvas;
               
double NewX = Math.Cos(_angles[i]) * RadiusX + CenterX;                         //计算x坐标
               double NewY = Math.Sin(_angles[i]) * RadiusY + CenterY;                         //计算y坐标
               aCanvas.SetValue(Canvas.LeftProperty, NewX);                                   //对图片集合中的每张图片进行重新定位
               aCanvas.SetValue(Canvas.TopProperty, NewY);
               _angles[i] 
+= _speed;                                                          //根据速度改变离心角的角度
               aCanvas.SetValue(Canvas.ZIndexProperty, System.Convert.ToInt32(NewY));         //设置图片的zIndex属性,防止位于底层的图片遮挡上层的图片
               double opacity = Math.Sin(_angles[i]) + 1.5;                                   //改变图片透明度,其实这里是改变画布透明度
               aCanvas.SetValue(Canvas.OpacityProperty, opacity);
               var curr 
= scaleTransforms[i];
               var change 
= NewY  / (CenterY + RadiusY - curr.ScaleY);                        //重新计算画布大小
               curr.ScaleX = change;                                     
               curr.ScaleY 
= change;


            }
        
        }

 

最后,把SetMove()函数放到dispatchertimer的tick触发函数中,每隔25ms 调用,更新每张图片的位置和大小及其透明度

 

void _disp_Tick(object sender, EventArgs e)
        {
            SetMove();
        }

 

 

这样一个旋转图片组的效果就做好了 :)

 

总结:这次其实没有直接去控制图片的位置,而是把图片放到画布中,通过改变画布的位置而改变图片的位置,这样做的好处是,可复用性更强一些,以后不只是做图片,可以把文本,按钮,或者其它控件甚至视频放到其中,产生这样的效果。当然,更好的做法是自己把它封装成控件,以便以后更方便使用,另外,这个效果目前还是比较基础和粗糙的,还有很多地方可以改进,例如,可以加入鼠标的交互,当鼠标移到上面不同位置产生加速,减速,改变方向,改变椭圆中心等等~

posted @ 2010-11-16 20:15  Sirk  阅读(6220)  评论(6编辑  收藏  举报