Silverlight:Mouse Avoiding 躲避鼠标效果

2018-01-22 14:32:21 浏览数 (1)

昨晚在一国外博客上(从域名后缀pl上猜想应该是波兰)看到这种效果(Mouse Avoid 躲避鼠标),是基于Flash/AS3开发的,这个示例把弹性运动,摩擦力,均加速运动等多种物理学原理综合运用在一起,产生了不错的交互效果。

在线演示

as3.0代码如下:

代码语言:javascript复制
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;
		}

	}
}

测试代码:

代码语言:javascript复制
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:

代码语言:javascript复制
<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代码:

代码语言:javascript复制
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当作容器

代码语言:javascript复制
<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>

后端代码:

代码语言:javascript复制
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 人点赞