Silverlight:Mouse Avoiding 躲避鼠标效果

昨晚在一国外博客上(从域名后缀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版的原效果而言,没有运动时的拖尾效果。哪位仁兄帮忙改进下,谢谢。

源文件下载

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

扫码关注云+社区

领取腾讯云代金券