6.9.用鼠标拖拽对象

问题
我想要用鼠标随意拖动对象
解决办法
使用Sprite 的startDrop( ), stopDrag( ) 和dropTarget 实现拖拽效果。另外也可以继承ascb.display.DraggableSprite 类,使用drag( )和drop( ) 方法。
讨论
创建拖动效果并不是想象的那么困难。Sprite 类包括了这些拖动的方法,分别是startDrag( ) 和stopDrag( )。

startDrag( ) 方法可在任何Sprite 实例上调用,要想停止拖动则调用stopDrag( ) 方法,如果拖动完成,可以检查Sprite的dropTarget 属性是否是目标位置。

不需要指定任何参数就可调用startDrag( ),但实际上该方法有两个参数:
lockCenter
当为TRue 时Sprite的中心被鼠标位置锁定,不管鼠标是否按下。当为false 时只有鼠标点击它时Sprite 才会跟着移动,默认为false。
bounds
一个矩形范围来约束拖动,被拖动Sprite是不能超出这个范围,默认为null,意味着没有约束。

下面的代码使用这些方法建立了简单的拖动效果。有三个不同颜色的矩形,右边有个白色的矩形作为拖动的目标,当拖动左边的矩形到白色矩形上时,松开鼠标,白色矩形就会i改变相应颜色。:
+展开
-ActionScript
package {
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.filters.DropShadowFilter;
public class ColorDrop extends Sprite {
private var _red:Sprite;
private var _green:Sprite;
private var _blue:Sprite;
private var _white:Sprite;
// 保存被拖动对象原始坐标
private var startingLocation:Point;
// 创建矩形和添加事件
public function ColorDrop( ) {
createRectangles( );
addEventListeners( );
}
private function createRectangles( ):void {
_red = new Sprite( );
_red.graphics.beginFill( 0xFF0000 );
_red.graphics.drawRect( 0, 10, 10, 10 );
_red.graphics.endFill( );
_green = new Sprite( )
_green.graphics.beginFill( 0x00FF00 );
_green.graphics.drawRect( 0, 30, 10, 10 );
_green.graphics.endFill( );
_blue = new Sprite( );
_blue.graphics.beginFill( 0x0000FF );
_blue.graphics.drawRect( 0, 50, 10, 10 );
_blue.graphics.endFill( );
_white = new Sprite( );
_white.graphics.beginFill( 0xFFFFFF );
_white.graphics.drawRect( 20, 10, 50, 50 );
_white.graphics.endFill( );
addChild( _red );
addChild( _green );
addChild( _blue );
addChild( _white );
}
private function addEventListeners( ):void {
_red.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
_red.addEventListener( MouseEvent.MOUSE_UP, place );
_green.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
_green.addEventListener( MouseEvent.MOUSE_UP, place );
_blue.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
_blue.addEventListener( MouseEvent.MOUSE_UP, place );
}
public function pickup( event:MouseEvent ):void {
// 保存原始坐标以便回位
startingLocation = new Point( );
startingLocation.x = event.target.x;
startingLocation.y = event.target.y;
//开始拖动,给被拖动对象加上阴影
event.target.startDrag( );
event.target.filters = [ new DropShadowFilter( ) ];
// 把被拖动对象显示在最前面
setChildIndex( DisplayObject( event.target ), numChildren - 1 );
}
public function place( event:MouseEvent ):void {
// 停止拖动,取消阴影效果
event.target.stopDrag( );
event.target.filters = null;
// 检测是否已经被拖动到白色矩形上
if ( event.target.dropTarget == _white ) {
// 设置颜色
var color:uint;
switch ( event.target ) {
case _red: color = 0xFF0000; break;
case _green: color = 0x00FF00; break;
case _blue: color = 0x0000FF; break;
}
_white.graphics.clear( );
_white.graphics.beginFill( color );
_white.graphics.drawRect( 20, 10, 50, 50 );
_white.graphics.endFill( );
}
// 把被拖动对象放回原位
event.target.x = startingLocation.x;
event.target.y = startingLocation.y;
}
}
}

因为左边三个对象上都注册了mouseDown 和mouseUp 事件监听器,每次在相应对象上按下鼠标鼠标都会执行pickup 函数。

首先,保存被拖动对象的原始坐标,这样拖动完就能恢复位置。接着startDrag( ) 方法被调用开始拖动,而且加上了DropShadowFilter 效果,并且通过setChildIndex( ) 把被拖动对象设为最顶层。

当松开鼠标后mouseUp事件被激活,就会执行place( ) 方法。首先,调用stopDrag( ) 停止拖动,阴影效果被取消,接着检测拖动的目标是否为白色矩形,如果是就把被拖动对象的颜色应用在白色矩形上。然后恢复被拖动对象到原始位置上。

上面的代码看起来很好,但还是有两个小问题,那就是dropTarget 属性不总是可靠和稳定的。

在鼠标移动的过程中dropTarget 属性一直在不停的改变。这是对的,这样我们就能为不同的目标分别处理。并且可以分辨哪些目标才是我们需要的。但是dropTarget 的改变只有在鼠标点移动到新的物体上时才发生,也就是说当鼠标点离开物体它是不发生变化的。这就会有问题,比如当我们移动到一个物体上然后离开,但是并没有移动新的物体上,这时dropTarget 属性仍然指向原来的物体而不管鼠标已经离开物体了,这样就产生了偏差,请看下面:

看下面的情况:
捡起红色方框
移动到白色方框上
再移出白色方框
释放红色方框

我们看到白色方框的颜色还是变为了红色,这时因为dropTarget 仍然指向白色方框,虽然红色方框已经离开白色方框范围。

要修复这个问题,可使用hitTestPoint( ) 方法决定鼠标的位置是否在dropTarget 目标范围之内。

hitTestPoint( ) 方法接受x和y坐标,返回true或false。如果坐标在对象范围之内,还有第三个布尔型参数指定边界检测模式,false代表边界模式为矩形,true代表边界模式对象自身边界,默认为false。

我们在place( ) 方法的判断语句中增加hitTestPoint( )方法来检测dropTarget,来确认鼠标仍在白色方框范围之内:
+展开
-ActionScript
if ( event.target.dropTarget == _white
&& _white.hitTestPoint( _white.mouseX, _white.mouseY ) ) {

另一问题是当鼠标移动时屏幕更新会变得不稳定。这是因为鼠标事件不依赖于渲染处理。Flash帧数决定何时屏幕被刷新,因此如果鼠标改变了显示对象,但显示对象不会立即被更新,除非下一个帧到来(帧速决定快慢)。

为了解决这个问题,MouseEvent 类提供一个方法updateAfterEvent( )。当mouseMove 事件触发,updateAfterEvent( ) 同志Flash 播放器屏幕已改变指示必须重画,这样就解决了帧速所造成的延时问题。

但不幸的是updateAfterEvent( ) 和startDrag( )使用时效果总是不好。即使mouseMove 事件处理中调用了updateAfterEvent( )促使渲染器更新,但是调用updateAfterEvent( )后却没有效果。另一个我问题startDrag( ) 一次只能拖动一个Sprite。虽然这不是大问题,但也是一个限制。

为了处理以上问题,本书自定义了一个类叫DraggableSprite,在ascb.display 包中。

DraggableSprite 类继承自Sprite,提供了drag( )和drop( )。drag( )方法和startDrag( )一样接受同样的参数,使用方法也一样,drop( ) 方法和stopDrag( )一样。

DraggableSprite最大的不同是实现了自定义鼠标跟踪代码,多个DraggableSprite 实例可同时拖动,而且updateAfterEvent( ) 也解决了渲染延时问题。

但是在DraggableSprite里dropTarget 属性不能用了,代替它的是getObjectsUnderPoint( ) 方法,返回鼠标位置下面的对象。

getObjectsUnderPoint( ) 方法返回一个对象数组,在数组的length-1位置上的对象就是最底层的对象,0位置是最上层的对象,根据这个数组然后检测白色方框是否在内就知道拖动目标是否合法了。

下面的代码和以前的基本一致,只不过Sprite换成了DraggableSprite:
+展开
-ActionScript
package {
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.filters.DropShadowFilter;
import ascb.display.DraggableSprite;
public class ColorDrop extends Sprite {
private var _red:DraggableSprite;
private var _green:DraggableSprite;
private var _blue:DraggableSprite;
private var _white:Sprite;
// Saves the starting coordinates of a dragging Sprite so
// it can be placed back
private var startingLocation:Point;
// Create the rectangles that comprise the interface
// and wire the mouse events to make them interactive
public function ColorDrop( ) {
createRectangles( );
addEventListeners( );
}
private function createRectangles( ):void {
_red = new DraggableSprite( );
_red.graphics.beginFill( 0xFF0000 );
_red.graphics.drawRect( 0, 10, 10, 10 );
_red.graphics.endFill( );
_green = new DraggableSprite( )
_green.graphics.beginFill( 0x00FF00 );
_green.graphics.drawRect( 0, 30, 10, 10 );
_green.graphics.endFill( );
_blue = new DraggableSprite( );
_blue.graphics.beginFill( 0x0000FF );
_blue.graphics.drawRect( 0, 50, 10, 10 );
_blue.graphics.endFill( );
_white = new DraggableSprite( );
_white.graphics.beginFill( 0xFFFFFF );
_white.graphics.drawRect( 20, 10, 50, 50 );
_white.graphics.endFill( );
addChild( _red );
addChild( _green );
addChild( _blue );
addChild( _white );
}
private function addEventListeners( ):void {
_red.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
_red.addEventListener( MouseEvent.MOUSE_UP, place );
_green.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
_green.addEventListener( MouseEvent.MOUSE_UP, place );
_blue.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
_blue.addEventListener( MouseEvent.MOUSE_UP, place );
}
public function pickup( event:MouseEvent ):void {
// Save the original location so you can put the target back
startingLocation = new Point( );
startingLocation.x = event.target.x;
startingLocation.y = event.target.y;
// Start dragging the Sprite that was clicked on and apply
// a drop shadow filter to give it depth
event.target.drag( );
event.target.filters = [ new DropShadowFilter( ) ];
// Bring the target to front of the display list so
// that it appears on top of everything else
setChildIndex( DisplayObject( event.target ), numChildren - 1 );
}
public function place( event:MouseEvent ):void {
// Stop dragging the Sprite around and remove the depth
// effect from the filter
event.target.drop( );
event.target.filters = null;
// Get a list of objects inside this container that are
// underneath the mouse

var dropTargets:Array = getObjectsUnderPoint( new Point( mouseX, mouseY ) );
// The display object at position length - 1 is the top-most object,
// which is the rectangle that is currently being moved by the mouse.
// If the white rectangle is the one immedialy beneath that, the
// drop is valid
if ( dropTargets[ dropTargets.length - 2 ] == _white ) {
// Determine which color was dropped, and apply that color
// to the white rectangle
var color:uint;
switch ( event.target ) {
case _red: color = 0xFF0000; break;
case _green: color = 0x00FF00; break;
case _blue: color = 0x0000FF; break;
}
_white.graphics.clear( );
_white.graphics.beginFill( color );
_white.graphics.drawRect( 20, 10, 50, 50 );
_white.graphics.endFill( );
}
// Place the dragging Sprite back to its original location
event.target.x = startingLocation.x;
event.target.y = startingLocation.y;
}
}
}

加支付宝好友偷能量挖...


评论(0)网络
阅读(200)喜欢(0)flash/flex/fcs/AIR