转 第七章 交互动画:移动物体(as3.0)

我们最初的目标就是要制作出流畅的交互动画,多数都是通过鼠标进行交互的。在第二
章里面曾介绍过鼠标事件,但没有涉及到具体的应用。
    本章将踏出交互动画的第一步。我们将学会如何处理拖拽,抛落及投掷。但首先要从基
本的鼠标按下与释放说起。

 

按下及释放影片
    鼠标可真是件了不起的发明,虽说只是个简单的设备。实际上鼠标只负责两件事:检测
移动及点击按钮。计算机用获得的这些信息可以做很多事:通过获知鼠标指针的位置,确定
当发生点击的位置,移动的速度,及确定双击事件的发生。当我们从事件的角度来看这些问
题时,可以归结为点击与移动(当然,现在的鼠标还配有滚轮,跟踪球或是比一台廉价手机
还多的按钮,但现在我们考虑最基本的鼠标种类)。
    一次点击事件可分为两部分:鼠标键按下时的事件及鼠标弹上来的事件。通常情况下,
这两个事件是在一瞬间发生的。有些时候,这两个事件会被时间和移动分隔开,通常解释为
拖拽——按下,移动,最后释放。本章就围绕下面三件事展开:鼠标按下,鼠标弹起,以及
发生在它们中间的移动。
    对于鼠标事件的处理,在 AS 3 中确实发生了很大的变化,所以需要全面地重新学习一
下这些基础问题,拥有坚实的基础是非常重要的。AS 3 事件体系的结构非常合理也非常科
学,而早先版本的 AS 有时看起来就像巫术一样。
    鼠标事件只能由 Sprite 影片,影片剪辑或其它交互对象在鼠标经过它们的图形时产
生。在 AS 2 中,这些也许只对某些鼠标事件起作用,而对其它的却不起作用,这就显得非
常混乱。同时也使如 onRelase 和 onReleaseOutside 这样的复合事件成为必需品。
    另一个本质上的改变是在嵌套对象与鼠标事件之间的。在 AS 2 中,没有办法使用影片
剪辑内部的影片来侦听事件。外层的影片剪辑可以捕获所有的鼠标事件,而后事件就不再向
下流通。而在 AS 3 中,就没有这些限制,使用影片剪辑或 Sprite 影片或嵌套影片进行侦
听都没有问题。
    需要注意的是主要影片事件是 mouseDown, mouseUp, 和 mouseMove。它们都被制作成
了 MouseEvent 类的静态属性:
■ MouseEvent.MOUSE_DOWN
■ MouseEvent.MOUSE_UP
■ MouseEvent.MOUSE_MOVE
    mouseDown 事件,是当鼠标指针处于某个影片的图形时,按下鼠标后发生的。  等同于 AS
2 中的 onPress。
    mouseUp 事件,是当鼠标指针处于某个影片的图形时,释放鼠标后发生的。  等同于 AS 2
中的 onRelease。
    mouseMove 事件,是当鼠标移动时发生的——但只在鼠标移动到该物体或影片时才发
生。这点与 AS 2 中不同,在 AS 2 中使用 onMouseMove 时,无论鼠标何时移动,无论指
针在哪,都会将这个事件的信息传达给所有的影片剪辑。
    然而,有时我们希望在忽略指针位置的情况下,侦听鼠标移动、弹起或按下。 在
AS 2 中,使用 onMouseMove, onMouseUp 和 onMouseDown ,都不会关注鼠标的位置。而在

AS 3 中,虽然这些方法有所不同,但只要使用 stage 来侦听 mouseDown, mouseUp 和

mouseMove,同样会将事件信息传达给所有的影片剪辑。
    OK,说了不少,让我们先来看个例子吧。本章第一个示例,文档类 MouseEvents.as,
继续使用前几章的 Ball 类,代码如下:

 

package {
 import flash.display.Sprite;
 import flash.events.MouseEvent;
 public class MouseEvents extends Sprite {
  public function MouseEvents() {
    init();
  }
  private function init():void {
    var ball:Ball=new Ball ;
    ball.x=100;
    ball.y=100;
    addChild(ball);
    ball.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDownBall);
    ball.addEventListener(MouseEvent.MOUSE_UP,onMouseUpBall);
    ball.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMoveBall);
    stage.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDownStage);
    stage.addEventListener(MouseEvent.MOUSE_UP,onMouseUpStage);
    stage.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMoveStage);
  }
  private function onMouseDownBall(event:MouseEvent):void {
    trace("mouse down - ball");
  }
  private function onMouseUpBall(event:MouseEvent):void {
    trace("mouse up - ball");
  }
  private function onMouseMoveBall(event:MouseEvent):void {
    trace("mouse move - ball");
  }
  private function onMouseDownStage(event:MouseEvent):void {
    trace("mouse down - stage");
  }
   private function onMouseUpStage(event:MouseEvent):void {
     trace("mouse up - stage");
   }
   private function onMouseMoveStage(event:MouseEvent):void {
     trace("mouse move - stage");
   }
  }
}
      这个类只不过是建立了前面说过的三种鼠标事件的处理函数,先为 ball 建立侦听,再
为 stage 建立侦听。通过这些可以让我们知道事件是何时发生的。大家可以通过这个文件
来明白到底什么时候什么地方会触发这些事件,有些需要注意的地方:
■ ball 事件只发生在鼠标经过 ball 的时候。
■ 特别要注意, ball 的 mouseMove 事件只在鼠标经过 ball 的时候才发生。
■ 无论鼠标在哪,都会获得 stage 事件——即使经过 ball 的时候也会发生。这样一来,
就会得到两个事件——一个是 ball 的,一个是 stage 的。
■ 不可能在没有 mouseDown 事件的情况下,就出现了 mouseUp 事件。
      现在大家已经掌握了本章的一些重要事件的基础,下面开始学习拖拽。

 

拖拽影片
    拖拽影片有两种方法:使用 mouseMove 事件或使用 startDrag/stopDrag 方法。本章
会介绍这两种方法,首先学习如何使用 mouseMove 实现拖拽,这会给大家一些处理
mouseMove 事件的经验,让各位更深入的理解事件是如何运行的。
使用 mouseMove 执行拖拽
    通过手动处理 mouseMove 事件,可以更新影片的位置,使影片每次都移动到鼠标指针
的位置,这种方法是每次移动多个影片对象的唯一方法。在使用某个影片作为鼠标指针时,
常会用到这种方法。一个影片跟随鼠标的位置,如果这时还要拖拽其它的影片,该怎么办?
解决办法就是使用 mouseMove 作鼠标跟随,而不是常用的拖拽方法(startDrag/stopDrag)。
因此,这是一个非常实用的技术。
    基本策略是:在 mouseDown 事件中,建立一个 mouseMove 处理函数。这个处理函数用
于将 ball 的 x,y 坐标赋给当前鼠标的位置。在 mouseUp 事件中,再移除这个处理函数。
    这里有一个技巧,我们要为移动的影片设置 mouseDown 侦听器,而对其它两个事件则
在 stage 中进行侦听。因为,有时可能拖拽的速度比 Flash 刷新影片位置的速度要快。这
样一来,如果用影片侦听 mouseUp 和 mouseMove 事件,将有可能不会响应,下一时刻也许
鼠标指针就已经不在影片上了。请记住,无论鼠标在哪儿, stage 都将把这些事件发送出
去。下一个文档类,MouseMoveDrage.as,看过后会更加清楚:
package {
 import flash.display.Sprite;
 import flash.events.MouseEvent;
 public class MouseMoveDrag extends Sprite {
  private var ball:Ball;
  public function MouseMoveDrag() {
    init();
  }
  private function init():void {
    ball = new Ball();
    ball.x = 100;
    ball.y = 100;
    addChild(ball);
    ball.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
  }
  private function onMouseDown(event:MouseEvent):void {
    stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
    stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
  }
  private function onMouseUp(event:MouseEvent):void {
    stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
    stage.removeEventListener(MouseEvent.MOUSE_MOVE,
    onMouseMove);
  }
  private function onMouseMove(event:MouseEvent):void {
    ball.x = mouseX;
    ball.y = mouseY;
  }
  }
}
    最初,只在 ball 上侦听 mouseDown 事件。然后,在 onMouseDown 方法中增加 stage
的侦听器,用于侦听 mouseUp 和 mouseMove 事件。 onMouseMove 方法将 ball 的位置更新
为鼠标位置。当不再使用拖拽时,通过 onMouseUp 方法移除从 stage 中移除 mouseUp 和
mouseMove 侦听器。 从这些设置中也许大家已经发现了一个问题,当点击到 ball 的边缘进
行拖拽时,我们发现 ball 突然将中心点对齐到鼠标指针位置上了。这是因为我们设置 ball
的 x,y 坐标完全等于鼠标坐标。大家可以在点击鼠标时,找出鼠标与 ball 的偏移量,然
后在进行拖拽时将它加入小球的位置上。把它留做练习,如果大家感兴趣的话不防试一下。
下面来看看拖拽影片的常用方法。

 

使用 startDrag/stopDrag 执行拖拽
     所有的 Sprite 影片和影片剪辑都有名为 startDrag 和 stopDrag 的内置方法,用于
实现拖拽。这种方法唯一的缺点就是一次只能拖拽一个对象。
     概念非常简单,在 mouseDown 处理函数中调用 starDrag。在 mouseUp 处理函数中调
用 stopDrag。
     调用 starDrag 时可以没有参数,我们将前一个文档类中的 onMouseDown 和
onMouseUp 方法,改为如下代码(或者见 Drag.as):
private function onMouseDown(event:MouseEvent):void {
  stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
  ball.startDrag();
}
private function onMouseUp(event:MouseEvent):void {
  stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
  ball.stopDrag();
}
     这样一来,可以删除 onMouseMove 方法,因为留在这里也没有用。
     太简单了吧,嗯?测试一下,发现 ball 的拖拽位置就是开始点击在 ball 上的位置,
没有变化。如果想让它跳到影片中心点上,可以在 startDrag 中传入参数 ture。
     也可以使用 left,top,right,bottom 坐标限制拖拽在一个矩形区域内进行。在 AS 3
中有一些变化,要传入一个 Rectangle 对象(flash.geom.Rectangle 类的实例,不要忘了
导入)作为参数。在 Rectangle 的构造函数中传入这四个参数,方法如下:
var rect:Rectangle = new Rectangle(10, 10, 200, 200);
这是 startDrag 的全部语法:
startDrag(锁定中心, 矩形边界)
使用下面这条语句改变上一个例子中的 startDrag:
ball.startDrag(false, new Rectangle(100, 100, 300, 300));
再提醒一句,不要忘了导入 Rectangle 类!
结合运动代码的拖拽
    目前为止我们已经完全可以在 Flash 中实现简单的拖拽与抛落动画了。但是,如果不
对物体进行拖拽的话,那么它们又会静静地呆在那儿。下面让我们在动画中加入一些如速度
向量、加速度或弹力等内容。在前一章的 Bouncing2.as 文档类中,已经完成了速度向量,
重力和反弹。这是个不错的开始,再在里面加入拖拽与抛落的代码似乎会非常合理,来试一
下。做出的结果应该与下面这些代码相似(DragAndMove1.as):
package {
  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.events.MouseEvent;
  public class DragAndMove1 extends Sprite {
   private var ball:Ball;
   private var vx:Number;
   private var vy:Number;
   private var bounce:Number=-0.7;
   private var gravity:Number=.5;
   public function DragAndMove1() {
     init();
   }
   private function init():void {
     stage.scaleMode=StageScaleMode.NO_SCALE;
     stage.align=StageAlign.TOP_LEFT;
     ball=new Ball ;
     ball.x=stage.stageWidth / 2;
     ball.y=stage.stageHeight / 2;
     vx=Math.random() * 10 - 5;
     vy=-10;
     addChild(ball);
     ball.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
     addEventListener(Event.ENTER_FRAME,onEnterFrame);
   }
   private function onEnterFrame(event:Event):void {
vy+= gravity;
ball.x+= vx;
ball.y+= vy;
var left:Number=0;
var right:Number=stage.stageWidth;
var top:Number=0;
var bottom:Number=stage.stageHeight;
if (ball.x + ball.radius > right) {
 ball.x=right - ball.radius;
 vx*= bounce;
} else if (ball.x - ball.radius < left) {
 ball.x=left + ball.radius;
 vx*= bounce;
}
if (ball.y + ball.radius > bottom) {
 ball.y=bottom - ball.radius;
 vy*= bounce;
} else if (ball.y - ball.radius < top) {
 ball.y=top + ball.radius;
 vy*= bounce;
     }
   }
    private function onMouseDown(event:MouseEvent):void {
      stage.addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
      ball.startDrag();
    }
    private function onMouseUp(event:MouseEvent):void {
      stage.removeEventListener(MouseEvent.MOUSE_UP,onMouseUp);
      ball.stopDrag();
    }
  }
}
     这里只是在原先的代码中加入了 onMouseDown 和 onMouseUp 处理函数。
     测试后,大家很快就会发现在执行拖拽时存在着一些问题。是的,拖拽确实存在,但同
时还伴随着物体运动的代码,感觉就像是小球从手中脱落一样。为了让拖拽时不产生移动,
需要一些方法来开启或关闭执行移动的代码。最简单的方法就是在开始拖拽时,删除
enterFrame 函数,而在停止拖拽时再将它设置回来。这种方法只在 onMouseDown 和
onMouseUp 方法中多加了几行代码:
private function onMouseDown(event:MouseEvent):void {
  stage.addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
  ball.startDrag();
  removeEventListener(Event.ENTER_FRAME,onEnterFrame);
}
private function onMouseUp(event:MouseEvent):void {
  stage.removeEventListener(MouseEvent.MOUSE_UP,onMouseUp);
  ball.stopDrag();
  addEventListener(Event.ENTER_FRAME,onEnterFrame);
}
     测试后,感觉已经很接近理想的效果了,但还有些小问题。拖拽物体时,只是进行拖拽,
当抛落小球时, 运动代码会从上一次离开时的位置继续开始运动。原因是物体的速度向量依
然保持离开时的状态。这就会使鼠标释放时,小球还保持向某个方向移动,显得很不自然。
这样,我们只需要将 vx 和 vy 设置为零,写在拖拽或释放时都可以。让我们将它放入执行
拖拽的函数中:
private function onMouseDown(event:MouseEvent):void {
    vx = 0;
    vy = 0;
    stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
    ball.startDrag();
    removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
    这样问题基本都解决了,结合速度向量,加速度和反弹,实现了完整的拖拽与抛落的特
征。全部代码见文档类 DragAndMove2.as。
    这里还留下一个问题;当抛落小球时,它是垂直下落的—— x 轴上没有运动。虽说这
个动作没有问题,但是有些单调。如果可以将小球抛出去,并让它在抛出的方向运动,那将
是个很棒的交互运动效果。好的,下面就来看一下,它是如何实现的。

 


投掷
  在 Flash 中,投掷是一个什么概念?它意味着当我们点击一个对象时开始对它进行拖拽,
再沿某个方向移动一段距离,松开鼠标后,物体沿拖拽的方向继续移动。
  在拖拽的过程中,需要确定为移动物体所需的速度向量,然后在鼠标释放后,将这个速
度向量的值再赋给物体。换句话讲,如果每帧向左拖拽影片 10 个像素的话,当鼠标松开时,
它的速度向量应该是 vx = -10。
  设置新的速度向量来对大家来说没有问题。只需要将 vx,vy 的值赋给物体,如图 7-1 所
示,但是如何确定这两个数值还需要一点小技巧。事实上,计算拖拽的距离与应用速度向量
的原理是相互对应的。在应用速度向量时,我们将速度向量加到物体原坐标的位置上,从而
形成新的位置,所以这个公式可以写成:旧的位置 + 速度向量 = 新的位置。为了计算出
移动的速度向量,只需要将这个等式做一个简单的变形:速度向量 = 新位置 – 旧位置。
   在拖拽影片时,会在每一帧形成新的位置。用当前帧的位置减去前一帧的位置,就可知
道这一帧所移动的距离。这就是每帧移动距离的速度向量!
   我们来做一个例子,将问题简化为在一个轴上拖拽影片。在某一帧上,知道物体的 x 位
置是 150。下一帧时,知道它的 x 位置是 170。因此,这一帧内,物体在 x 轴上移动了 20
像素,那么物体的 x 速度向量就是 +20。当鼠标释放后,需要物体还能按 x 轴为 20 的速
度,继续运动。所以,应该设 vx = 20。
   这就需要对前面的类做一些改变。首先,需要知道每一帧的速度向量,因此就需要加入
enterFrame 事件的处理函数。需要创建一个新的方法用来计算拖拽的速度向量,这个方法
就叫 trackVelocity。 同时还需要两个变量来存储旧的 x,y 位置,可以将它们命名为 oldX 和
oldY,并将它们声明为类变量。一旦开始拖拽,就需要知道小球的位置并存储为 oldX 和
oldY。在移除了 onEnterFrame 后,加入 trackVelocity 作为侦听器。onMouseDown 方法的
完整内容如下:
private function onMouseDown(event:MouseEvent):void {
   oldX = ball.x;
   oldY = ball.y;
   stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
   ball.startDrag();
   removeEventListener(Event.ENTER_FRAME, onEnterFrame);
   addEventListener(Event.ENTER_FRAME, trackVelocity);
}
   然后,在 trackVelocity 方法中,把 oldX 和 oldY 从当前位置中减去。这样就求出了当
前的速度向量,可以在 vx 和 vy 中直接保存它们。随后再重新设置 oldX 和 oldY 为小球
的当前坐标。
private function trackVelocity(event:Event):void {
   vx = ball.x - oldX;
   vy = ball.y - oldY;
   oldX = ball.x;
   oldY = ball.y;
}
最后,当释放小球时,再交换 enterFrame 函数 。这时将 trackVelocity 移除,再加入
onEnterFrame:
private function onMouseUp(event:MouseEvent):void {
    stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
    ball.stopDrag();
    removeEventListener(Event.ENTER_FRAME, trackVelocity);
    addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
    拖拽的同时速度向量就已经计算出来了,保存在 vx 和 vy 中。在开启移动代码时,小
球将会沿拖拽的方向移动。结果就是:将小球投掷出去!这里是最后一个文档类
(Throwing.as):
package {
 import flash.display.Sprite;
 import flash.display.StageAlign;
 import flash.display.StageScaleMode;
 import flash.events.Event;
 import flash.events.MouseEvent;
 public class Throwing extends Sprite {
  private var ball:Ball;
  private var vx:Number;
  private var vy:Number;
  private var bounce:Number = -0.7;
  private var gravity:Number = .5;
  private var oldX:Number;
  private var oldY:Number;
  public function Throwing() {
   init();
  }
  private function init():void {
   stage.scaleMode = StageScaleMode.NO_SCALE;
   stage.align=StageAlign.TOP_LEFT;
   ball = new Ball();
   ball.x = stage.stageWidth / 2;
   ball.y = stage.stageHeight / 2;
   vx = Math.random() * 10 - 5;
 vy = -10;
 addChild(ball);
 ball.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
 addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
 vy += gravity;
 ball.x += vx;
 ball.y += vy;
 var left:Number = 0;
 var right:Number = stage.stageWidth;
 var top:Number = 0;
 var bottom:Number = stage.stageHeight;
 if (ball.x + ball.radius > right) {
  ball.x = right - ball.radius;
  vx *= bounce;
 } else if (ball.x - ball.radius < left) {
  ball.x = left + ball.radius;
  vx *= bounce;
 }
 if (ball.y + ball.radius > bottom) {
  ball.y = bottom - ball.radius;
  vy *= bounce;
 } else if (ball.y - ball.radius < top) {
  ball.y = top + ball.radius;
  vy *= bounce;
 }
}
private function onMouseDown(event:MouseEvent):void {
 oldX = ball.x;
 oldY = ball.y;
 stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
 ball.startDrag();
 removeEventListener(Event.ENTER_FRAME, onEnterFrame);
 addEventListener(Event.ENTER_FRAME, trackVelocity);
}
private function onMouseUp(event:MouseEvent):void {
 stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
 ball.stopDrag();
 removeEventListener(Event.ENTER_FRAME, trackVelocity);
 addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function trackVelocity(event:Event):void {
 vx = ball.x - oldX;
 vy = ball.y - oldY;
 oldX = ball.x;
 oldY = ball.y;
}
 }
}
   这是个相当厉害的交互动画,在 Flash 中重现了现实世界的物理学。试改变 gravity,
bounce 等变量的值,观察变化的情况。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值