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