昨晚在一國外部落格上(從域名字尾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版的原效果而言,沒有運動時的拖尾效果。哪位仁兄幫忙改進下,謝謝。
作者:菩提樹下的楊過