天天看點

Silverlight:Mouse Avoiding 躲避滑鼠效果

昨晚在一國外部落格上(從域名字尾pl上猜想應該是波蘭)看到這種效果(Mouse Avoid 躲避滑鼠),是基于Flash/AS3開發的,這個示例把彈性運動,摩擦力,均加速運動等多種實體學原理綜合運用在一起,産生了不錯的互動效果。

Silverlight:Mouse Avoiding 躲避滑鼠效果

線上示範

as3.0代碼如下:

package {
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.geom.Point;

  public class MouseAvoider extends Sprite {

    private const SPRING:Number=0.1;//彈性系數
    private const FRICTION:Number=0.9;//摩擦系數
    private const FEAR_DISTANCE:Number=150;//安全距離(小于該距離則發生躲避行為)
    private const MAX_AVOID_FORCE:uint=10;//最大躲避速度

    private var _destinationPoint:Point;//目标靜止點(滑鼠遠離該物體時,物體最終會靜止的坐标點)
    private var _speed:Point=new Point(0,0);//速度矢量(_speed.x即相當于vx,_speed.y即相當于vy)

    public function MouseAvoider():void {
      drawStuff();
    }

    private function drawStuff():void {
      //預設先畫一個半徑為10的圓
      graphics.beginFill(Math.random() * 0xFFFFFF);
      //graphics.beginFill(0xFFFFFF);
      graphics.drawCircle(0, 0, 5);
      graphics.endFill();
    }

    //隻寫屬性(設定目标點)
    public function set destinationPoint(value:Point):void {
      _destinationPoint=value;
      addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }
    
    
    protected function enterFrameHandler(e:Event):void {
      moveToDestination();//先移動到目标點
      avoidMouse();//躲避滑鼠
      applyFriction();//應用摩擦力
      updatePosition();//更新位置
    }

    //以彈性運動方式移動到目标點
    private function moveToDestination():void {
      _speed.x += (_destinationPoint.x - x) * SPRING;
      _speed.y += (_destinationPoint.y - y) * SPRING;
    }

    //躲避滑鼠
    private function avoidMouse():void {
      var currentPosition:Point=new Point(x,y);//确定目前位置
      var mousePosition:Point=new Point(stage.mouseX,stage.mouseY);//确實滑鼠位置
      var distance:uint=Point.distance(currentPosition,mousePosition);//計算滑鼠與目前位置的距離
      
      //如果低于安全距離
      if (distance<FEAR_DISTANCE) {
        var force:Number = (1 - distance / FEAR_DISTANCE) * MAX_AVOID_FORCE;//計算(每機關時間的)躲避距離--即躲避速率
        var gamma:Number=Math.atan2(currentPosition.y- mousePosition.y,currentPosition.x- mousePosition.x);//計算滑鼠所在位置與目前位置所成的夾角
        
        var avoidVector:Point=Point.polar(force,gamma);//将極坐标轉換為普通(笛卡爾)坐标--其實相當于vx = force*Math.cos(gamma),vy = force*Math.sin(gamma)
        
        //加速 躲避逃開
        _speed.x+=avoidVector.x;
        _speed.y+=avoidVector.y;
      }
    }
    
    //應用摩擦力
    private function applyFriction():void {

      _speed.x*=FRICTION;
      _speed.y*=FRICTION;
    }
    
    //最終更新自身的位置
    private function updatePosition():void {

      x+=_speed.x;
      y+=_speed.y;
    }

  }
}
      

測試代碼:

package {
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.Sprite;
  import flash.filters.BlurFilter;
  import flash.geom.ColorTransform;
  import flash.geom.Point;
  import flash.events.Event;
  import flash.geom.Rectangle;

  /**
  * ...
  * @author Andrzej Korolczuk
  */
  [SWF(height="300",width="400")]
  public class MouseAvoiderTest extends Sprite {

    private var background:Bitmap;
    private var backgroundBitmapData:BitmapData;
    private var container:Sprite = new Sprite();
    private var bounds:Rectangle;
    private var blur:BlurFilter=new BlurFilter(5,5);
    private var colorTransform:ColorTransform=new ColorTransform(0.9,0.9,0.9);


    public function MouseAvoiderTest() {
      addEventListener(Event.ADDED_TO_STAGE, init);
    }

    private function init(e:Event):void {
      bounds=new Rectangle(0,0,stage.stageWidth,stage.stageHeight);
      addChild(container);
      backgroundBitmapData=new BitmapData(stage.stageWidth,stage.stageHeight,true,0xff000000);
      background=new Bitmap(backgroundBitmapData);
      addChild(background);
      var i:uint=7;
      while (i--) {
        addAvoider();
      }
      addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }

    //建立MouseAvoider執行個體
    private function addAvoider():void {
      var avoider:MouseAvoider = new MouseAvoider();
      container.addChild(avoider);
      //随機設定目标點
      avoider.destinationPoint = new Point(100+(stage.stageWidth-200) * Math.random(), 100+(stage.stageHeight-200)  * Math.random());
    }

    private function enterFrameHandler(e:Event):void {
      backgroundBitmapData.draw(container);//根據container的内容生成位圖資料
      backgroundBitmapData.applyFilter(backgroundBitmapData, bounds, new Point(0, 0), blur);//應用模糊濾鏡
      backgroundBitmapData.colorTransform(bounds, colorTransform);//應用顔色濾鏡(r,g,b顔色值均變成原來的90%)
      for (var i:uint = 0; i < container.numChildren; i++) {
        var avoider:MouseAvoider=container.getChildAt(i) as MouseAvoider;
        //r,g,b三色分量的偏移量設定為随機數(這樣看上去就會不停的閃爍)
        avoider.transform.colorTransform=new ColorTransform(1,1,1,1,Math.random()*30,Math.random()*30,Math.random()*30);
      }
    }



  }

}
      

看完AS3的代碼後,我就在想如何移植到Silverlight上來,下午抽空研究了一下,基本上用Silverlight還原出來了,但由于Silverlight在Bitmap程式設計方面的功能有點弱,另外沒有Flash中的ColorTransForm顔色變換(也有可能是我沒找到silverlight中對應的方法),效果上還是有點差距

先定義一個控件MouseAvoider.xaml:

<UserControl x:Class="MouseAvoider.MouseAvoider"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="10" d:DesignWidth="10">
    
    <Canvas>
        <Ellipse Width="10" Height="10" Fill="White" StrokeThickness="0" x:Name="ball">
            <Ellipse.RenderTransform>
                <ScaleTransform x:Name="scale"></ScaleTransform>
            </Ellipse.RenderTransform>
        </Ellipse>        
    </Canvas>
</UserControl>
      

後端cs代碼:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace MouseAvoider
{
    public partial class MouseAvoider : UserControl
    {
        private double _spring = 0.1;
        private double _friction = 0.92;
        private int _fear_distance = 150;
        private int _max_avoid_force = 10;
        private Point _destinationPoint;
        private Point _speed = new Point(0, 0);


        public MouseAvoider()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(MouseAvoider_Loaded);
        }

        void MouseAvoider_Loaded(object sender, RoutedEventArgs e)
        {
            //如果要實作彩色,将下面代碼啟用即可
            //Random rnd = new Random();
            //System.Threading.Thread.Sleep(5);
            //this.ball.Fill = new SolidColorBrush(Color.FromArgb(255,(byte)rnd.Next(0,256),(byte)rnd.Next(0,256),(byte)rnd.Next(0,256)));
        }

        public Point DestinationPoint
        {
            set
            {
                _destinationPoint = value;
                CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
            }
        }

        private double X
        {
            get
            {
                return (double)this.GetValue(Canvas.LeftProperty);
            }
            set
            {
                this.SetValue(Canvas.LeftProperty, value);
            }
        }

        private double Y
        {
            get
            {
                return (double)this.GetValue(Canvas.TopProperty);
            }
            set
            {
                this.SetValue(Canvas.TopProperty, value);
            }
        }

        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            //以彈性運動方式移動到目标點
            _speed.X += (_destinationPoint.X - X) * _spring;
            _speed.Y += (_destinationPoint.Y - Y) * _spring;

            //躲避滑鼠
            Point currentPosition = new Point(X,Y);
            Point mousePosition = (App.Current.RootVisual as MainPage).MousePosition;           
            var dx = currentPosition.X - mousePosition.X;
            var dy = currentPosition.Y - mousePosition.Y;
            double distance = Math.Sqrt(dx * dx + dy * dy);           
            if (distance < _fear_distance) 
            {
                double force = (1 - distance / _fear_distance) * _max_avoid_force;
                double gamma = Math.Atan2(dy, dx);
                Point avoidVector = new Point(force * Math.Cos(gamma),force*Math.Sin(gamma));
                _speed.X += avoidVector.X;
                _speed.Y += avoidVector.Y;
            }

            //應用摩擦力
            _speed.X *= _friction;
            _speed.Y *= _friction;

            //更新位置
            X += _speed.X;
            Y += _speed.Y;
        }
    }
}
      

MainPage.xaml當作容器

<UserControl x:Class="MouseAvoider.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Canvas x:Name="stage" Width="400" Height="300" Background="Black">
        
    </Canvas>
</UserControl>
      

後端代碼:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;

namespace MouseAvoider
{
    public partial class MainPage : UserControl
    {

        Point _mousePosition = new Point(0, 0);

        public MainPage()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            stage.MouseMove += new MouseEventHandler(stage_MouseMove);
            Random rnd = new Random();
            BlurEffect blur = new BlurEffect();
            for (int i = 0; i < 7; i++)
            {               
                MouseAvoider _avoider = new MouseAvoider();
                _avoider.DestinationPoint = new Point(stage.Width / 2 - (rnd.NextDouble() * 2 - 1) * 80, stage.Height / 2 - (rnd.NextDouble() * 2 - 1) * 80);
                stage.Children.Add(_avoider);

                _avoider.Effect = blur;
            }

            CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
            
        }

        void CompositionTarget_Rendering(object sender, EventArgs e)
        {            
            Random rnd = new Random();
            for (int i = 0; i < stage.Children.Count; i++)
            {
                MouseAvoider _item = stage.Children[i] as MouseAvoider;
                _item.scale.ScaleX = _item.scale.ScaleY = 1.0 + (rnd.NextDouble() * 2 - 1) * 0.1;
            }
        }

        void stage_MouseMove(object sender, MouseEventArgs e)
        {
            _mousePosition = e.GetPosition(this.stage);            
        }

        /// <summary>
        /// 擷取目前滑鼠所在位置
        /// </summary>
        public Point MousePosition
        {
            get { return _mousePosition; }
        }
    }
}

      
Silverlight:Mouse Avoiding 躲避滑鼠效果

注:沒有找到Silverlight中對應的ColorTransForm方法,是以用白色替換了。同時相對Flash版的原效果而言,沒有運動時的拖尾效果。哪位仁兄幫忙改進下,謝謝。

作者:菩提樹下的楊過

上一篇: draw rect