昨晚在一国外博客上(从域名后缀pl上猜想应该是波兰)看到这种效果(Mouse Avoid 躲避鼠标),是基于Flash/AS3开发的,这个示例把弹性运动,摩擦力,均加速运动等多种物理学原理综合运用在一起,产生了不错的交互效果。
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中对应的ColorTransForm方法,所以用白色替换了。同时相对Flash版的原效果而言,没有运动时的拖尾效果。哪位仁兄帮忙改进下,谢谢。