AS3 Right mouse button drag & drop - actionscript-3

With Flash Player 11.2, we now have access to mouse events for the right mouse button:
MouseEvent.RIGHT_CLICK
MouseEvent.RIGHT_MOUSE_DOWN
MouseEvent.RIGHT_MOUSE_UP
However, when I try to use these to implement a right-mouse-button drag&drop, it seems that once the right mouse button is down, I no longer get MouseEvent.MOUSE_MOVE and the stage's MouseX and MouseY members stop updating until the button is released. This makes it effectively impossible to implement that drag&drop.
Use the code example in Flash Builder below to see what I mean:
Launch it, move your mouse around and you'll see logs of your mouse position updating.
Press and hold LMB, move your mouse around, you'll see more logs, great!
Release LMB, press and hold LMB, move your mouse around, no logs, bad!
Is this a limitation of AS3? I can't seem to find any documentation about it.
Code Example:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class TestFlash extends Sprite
{
public function TestFlash()
{
if(stage){
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onLMBDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onLMBUp);
stage.addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, onRMBDown);
stage.addEventListener(MouseEvent.RIGHT_MOUSE_UP, onRMBUp);
}
}
private function onMouseMove(event:MouseEvent) : void
{
trace("Mouse Pos, from event: x = " + event.localX + " y = " + event.localY + " from stage: x = " + stage.mouseX + " y = " + stage.mouseY);
}
private function onRMBDown(event:MouseEvent) : void
{
trace("RMB down");
}
private function onRMBUp(event:MouseEvent) : void
{
trace("RMB up");
}
private function onLMBDown(event:MouseEvent) : void
{
trace("LMB down");
}
private function onLMBUp(event:MouseEvent) : void
{
trace("LMB up");
}
}
}

Judging by the problem you are describing, I think you need to add the enter frame function, otherwise the trigger will only activate on the frame that the mouse is clicked and when its released
addEventListener(Event.ENTER_FRAME, dragdrop)
function dragdrop(event:Event):void {
"add dragging event here"
}

Related

How to move the character up when touched the stage in ActionScript 3

I am new to ActionScript 3 and yes it is quite interesting. But I have got a problem with touch event.
I have already coded for the character (bird) to move forward and down at each frame and now I need to insert a function to move the bird upwards when tapping on the screen. I have tried it with an example in this url - https://help.adobe.com/en_US/as3/dev/WS1ca064e08d7aa93023c59dfc1257b16a3d6-7ffe.html
But still nothing happens. Please help me.
My code is as below.
package{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.TouchEvent;
import flash.ui.Multitouch;
Multitouch.inputMode=MultitouchInputMode.TOUCH_POINT;
public class MyBirdy extends MovieClip{
public var bird: Birdy;
public var sky: Background;
public var sky2: Background;
public var birdinsky: MovieClip;
public var skyBreadth:Number;
public function MyBirdy(){
bird = new Birdy();
sky = new Background();
sky2 = new Background();
skyBreadth = 1453.15;
sky.x = 730;
sky.y = 360;
bird.x = 100;
bird.y = 340;
sky2.x = sky.x + skyBreadth;
sky2.y = sky.y;
birdinsky = new MovieClip();
birdinsky.addChild(sky);
birdinsky.addChild(sky2);
birdinsky.addChild(bird);
stage.addChild(birdinsky);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
public function onEnterFrame(e:Event):void{
bird.x += 4;
birdinsky.x = 100 - bird.x;
bird.y += 2
if(sky.x + skyBreadth + birdinsky.x < 700){
sky.x = sky.x + (2 * skyBreadth);
}
if(sky2.x + skyBreadth + birdinsky.x < 700){
sky2.x = sky2.x + (2 * skyBreadth);
}
birdinsky.addEventListener(TouchEvent.TOUCH_TAP, onTap);
}
function onTap(e:TouchEvent): void {
bird.y -= 2;
//I want my bird to fly up when tapped!
}
}
The reason it doesn't work, is because your tap movement is always going to be negated by the movement in the enter frame handler. e.g. you tap, which moves the bird up 2 pixels, but then on the next frame tick of your application (when you'd see the visual change) you move the bird down again 2 pixels in onEnterFrame - which runs every frame tick.
Here is a way to do what you'd like:
Take this line, and remove it completely (or if you actually want a tap and not a hold, move it out of the onEnterFrame method and into your constructor - you don't want to add a listener over and over again every frame)
birdinsky.addEventListener(TouchEvent.TOUCH_TAP, onTap);
If you don't need multiple simultaneous touch support, it would be simpler to use mouse events. remove this line if switching to mouse events instead of touch (though you could still use touch if you'd like):
Multitouch.inputMode=MultitouchInputMode.TOUCH_POINT;
In your constructor function (MyBirdy) add the following lines to listen for the mouse up and down events:
As suggested in the comments, you should listen on the stage for the mouse down (or touch begin) if you want a press anywhere on the screen to work
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); //or TouchEvent.TOUCH_BEGIN
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); //or TouchEvent.TOUCH_END
Then create the handler functions for those listeners, as well as a var to track the mouse button state:
private var isMouseDown:Boolean = false;
private function onMouseDown(e:Event):void {
isMouseDown = true;
}
private function onMouseUp(e:Event):void {
isMouseDown = false;
}
With the above you now have a var (isMouseDown) that will be set to true when the mouse is down, and false when the mouse is up. Remember that mouse here is also the same of a finger press/hold.
Now, inside your enter frame handler (onEnterFrame), add the following:
remove the line bird.y += 2.
Add:
if(isMouseDown){
bird.y -= 2; //move the bird up, as the screen is being pressed/clicked
}else{
bird.y += 2 //move the bird down
}
Now, instead of a single tap event, any frame where the mouse is down (or a press and hold) the bird will move up instead of down.

Coordinates don't change dragging scaled fxg graphics

I'm using FlashDevelop, actionscript 3 and fxg graphics build with Illustrator (exported as fxg 2.0).
I import a fxg rectangle build with Illustrator, then I set it draggable when you clik on it, so its coordinates change. This works if the rectangle scale is 1 (this means: no scale).
But, if I change its scale (ex. fxgRect.scaleX = fxgRect.scaleY = 0.5; ) you can drag it but its coordinates doesn't change!! And this make me crazy because the sprite changes position but if you ask its coordinates they aren't changed!!
And I set the fxgRect.mouseChildren = false; so I'm sure to move the sprite fxgRect and not something inside it.
(This is just an example, my aim is to build complex graphics with illustrator and use it in FlashDevelop).
Thank you in advance for help me.
This is the code done to test this problem. To use this example you have to create a fxg rectangle (fxgrectangle.fxg) and put it in the same folder of the SimpleDragFxg.as class.
import flash.display.*;
import flash.events.*;
import fxgrectangle;
public class SimpleDragFxg extends Sprite
{
public var fxgRect:Sprite = new fxgrectangle(); // make an istance of fxgRect
public function SimpleDragFxg()
{
//----------------------------------------------------------
addChild(fxgRect);
fxgRect.name = "FXGrect";
fxgRect.x = fxgRect.y = 50;
fxgRect.mouseChildren = false;
fxgRect.scaleX = fxgRect.scaleY = 0.5; //<<< this makes the problem
trace ("I'm ", this.name, "and I contain ", this.fxgRect.name, "which contains ", fxgRect.getChildAt(0).name);
fxgRect.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
//--------------------------------------------------------------
}
private function onMouseDown (e:MouseEvent):void
{
trace ("e.target: ", e.target.name);
if (DisplayObject(e.target).name == "FXGrect")
{
trace ("FXGrect position BEFORE drag: ", fxgRect.x, fxgRect.y);
Sprite(e.target).startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
}
private function onMouseUp (e:MouseEvent):void
{
fxgRect.stopDrag();
stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
if (DisplayObject(e.target).name == "FXGrect")
{
trace ("FXGrect position AFTER drag: ", fxgRect.x, fxgRect.y);
}
}
}
Try
public var fxgRect:Sprite = new Sprite();
fxgRect.addChild(new fxgrectangle());

Click event outside MovieClip in AS3

Is there any way to detect if the user click outside a MovieClip?
For instance, I need to detect it to close a previously opened menu (like Menu bar style: File, Edition, Tools, Help, etc).
How can I detect this kind of event? Thanks!
Add a listener to stage and check if stage is the target of the event.
Example of code here:
http://wonderfl.net/c/eFao
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class FlashTest extends Sprite
{
private var _menu : Sprite;
public function FlashTest()
{
_menu = new Sprite();
_menu.x = 100;
_menu.y = 100;
_menu.alpha = 0.5;
with(_menu.graphics)
{
beginFill(0xFF0000, 1);
drawRect(0, 0, 300, 300);
endFill();
}
addChild(_menu);
_menu.addEventListener(MouseEvent.CLICK, onClickHandler);
stage.addEventListener(MouseEvent.CLICK, onClickHandler);
}
private function onClickHandler(event : MouseEvent) : void
{
switch(event.target)
{
case _menu:
_menu.alpha = 0.5;
break;
case stage:
_menu.alpha = 1;
break;
}
}
}
}
You can add a listener to the click event of the root element:
MovieClip(root).addEventListener(MouseEvent.CLICK, clickObject);
then in the function clickObject, you can check to see what you are clicking.
function clickObject(e:Event):void
{
var hoverArray:Array = MovieClip(root).getObjectsUnderPoint(new Point(stage.mouseX, stage.mouseY));
var hoverOverObject:* = hoverArray[hoverArray.length - 1];
}
hoverOverObject references the element that you are clicking on. Often this will be the shape within the movie clip, so you'll need to look at it's parent then compare it to your movie clip. If the click wasn't on the drop down movie clip, trigger the close.
var container:MovieClip = new MovieClip();
var mc:MovieClip = new MovieClip();
with(mc.graphics){
beginFill(0xff0000,1);
drawCircle(0,0,30);
endFill();
}
mc.name = "my_mc";
container.addChild(mc);
addChild(container);
stage.addEventListener(MouseEvent.CLICK, action);
function action (e:MouseEvent):void
{
if(e.target.name != "my_mc"){
if(container.numChildren != 0)
{
container.removeChild(container.getChildByName("my_mc"));
}
}
}
Use capture phase:
button.addEventListener(MouseEvent.CLICK, button_mouseClickHandler);
button.stage.addEventListener(MouseEvent.CLICK, stage_mouseClickHandler, true);
//...
private function button_mouseClickHandler(event:MouseEvent):void
{
trace("Button CLICK");
}
private function stage_mouseClickHandler(event:MouseEvent):void
{
if (event.target == button)
return;
trace("Button CLICK_OUTSIDE");
}
Note that using stopPropagation() is good for one object, but failed for several. This approach works good for me.
Use a stage and a sprite (menu) click listener with the sprite listener executing first and apply the stopPropagation() method to the click handler of the sprite. Like this:
menu.addEventListener(MouseEvent.CLICK, handleMenuClick);
stage.addEventListener(MouseEvent.CLICK, handleStageClick);
private function handleMenuClick(e:MouseEvent):void{
// stop the event from propagating up to the stage
// so handleStageClick is never executed.
e.stopPropagation();
// note that stopPropagation() still allows the event
// to propagate to all children so if there are children
// within the menu overlay that need to respond to click
// events that still works.
}
private function handleStageClick(e:MouseEvent):void{
// put hide or destroy code here
}
The idea is that a mouse click anywhere creates a single MouseEvent.CLICK event that bubbles from the stage, down through all children to the target, then back up through the parents of the target to the stage. Here we interrupt this cycle when the target is the menu overlay by not allowing the event to propagate back up to the parent stage, ensuring that the handleStageClick() method is never invoked. The nice thing about this approach is that it is completely general. The stage can have many children underneath the overlay and the overlay can have its own children that can respond to clicks and it all works.

Own drag function in AS3

I need to develop my own drag function in AS3 (instead of using startDrag) because I'm resizing a MovieClip.
I'm doing this:
public class resizeBR extends MovieClip {
var initialScaleX, initialScaleY;
public function resizeBR() {
this.addEventListener(MouseEvent.MOUSE_DOWN, initResize);
this.addEventListener(MouseEvent.MOUSE_UP, stopResize);
}
public function initResize(e:MouseEvent):void
{
initialScaleX = e.target.scaleX;
initialScaleY = e.target.scaleY;
e.target.addEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
public function startResize(e:MouseEvent):void
{
e.target.x += e.localX;
e.target.y += e.localY;
e.target.parent.parent.width += mouseX;
e.target.parent.parent.height += mouseY;
// Keep its own scale
e.target.scaleX = initialScaleX;
e.target.scaleY = initialScaleY;
}
public function stopResize(e:MouseEvent):void
{
e.target.removeEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
}
But the drag feature is not working fluently. I mean, when I drag a MovieClip from class resizeBR I need to move slowly my mouse cursor or it's not going to work propertly.
resizeBR is a MovieClip as a child of another MovieClip; the second one is which I have to resize.
What am I doing wrong?
Thanks!
Thanks all for your answers, but I found a great classes to do what I want.
http://www.senocular.com/index.php?id=1.372
http://www.quietless.com/kitchen/transform-tool-drag-scale-and-rotate-at-runtime/
I'm not really sure if I completely understand what you mean. But I think your problem lies with your MOUSE_MOVE handler.
In your current example you're resizing your target only when moving your mouse over the target. When you're moving your mouse fast enough it's possible your mouse leaves the target, casuing it to stop resizing. When I'm writing my own drag handlers I usually set the MOUSE_MOVE and MOUSE_UP listeners to the stage.
Your class would end up looking something like this:
public class resizeBR extends MovieClip
{
var initialScaleX, initialScaleY;
public function resizeBR()
{
addEventListener(MouseEvent.MOUSE_DOWN, initResize);
addEventListener(MouseEvent.MOUSE_UP, stopResize);
}
public function initResize(e:MouseEvent):void
{
initialScaleX = scaleX;
initialScaleY = scaleY;
stage.addEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
public function startResize(e:MouseEvent):void
{
x += e.localX;
y += e.localY;
parent.parent.width += mouseX;
parent.parent.height += mouseY;
// Keep its own scale
scaleX = initialScaleX;
scaleY = initialScaleY;
}
public function stopResize(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
}
There are a couple reasons the resizing is jumpy. First, like rvmook points out, you'll need to make sure you support the mouse rolling off of the clip while its being resized. Since there is not an onReleaseOutside type of event in AS3, you have to set listeners to the stage, or some other parent clip. If you have access to the stage, that is best. If not, you can use the root property of your resizable clip, which will reference the highest level display object you have security access to. Setting mouse events to the root is a little wonky, because for them to fire, the mouse needs to be on one of the root's child assets - whereas the stage can fire mouse events when the mouse is over nothing but the stage itself.
Another reason you might be seeing some strange resizing behavior is because of using the localX/Y properties. These values reflect the mouseX/mouseY coordinates to the object being rolled over - which might not necessarily be your clip's direct parent.
I tend to avoid having classes access their parent chain. You might want to consider placing the resizing logic in the clip you want resized, and not in one of its children. Here is simple self resizing example:
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class ResizableBox extends MovieClip {
public function ResizableBox() {
addEventListener(MouseEvent.MOUSE_DOWN, startResize);
}
private function startResize(evt:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_MOVE, handleResize);
stage.addEventListener(MouseEvent.MOUSE_UP, stopResize);
}
private function stopResize(evt:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleResize);
stage.removeEventListener(MouseEvent.MOUSE_UP, stopResize);
}
private function handleResize(evt:MouseEvent):void {
this.scaleX = this.scaleY = 1;
this.width = this.mouseX;
this.height = this.mouseY;
}
}
}
ResizableBox is set as the base class of a MC in the library.

mouseX/Y confusion when dragging a child of a container

I've got a grid of images which are added to a imagecontainer(Sprite) in a class.
When I click a button, the imagecontainer gets tweened(scaled) to 0.2
Now I would like to start dragging around the images. on mouse-down I add an enterFrame Event:
function onEnterFrame(e:Event):void
{
imagecontainer.image.x = this.mouseX;
imagecontainer.image.y = this.mouseY;
}
Unfortunately the image is never on the mouseposition but has a increasing/decreasing offset to the mouse pointer.
The alternative, startDrag/stopDrag works perfectly, but I still need the mouseX/mouseY for placing the image on a grid…
I tried also parent.mouseX, no luck with that.
Why is this happening? I thought mouseX/mouseY is always depending on the stage-dimension.
have you tried:
function onEnterFrame(e:Event):void
{
imagecontainer.image.x = stage.mouseX;
imagecontainer.image.y = stage.mouseY;
}
If you want to get the mouseX and mouseY relative to the mouse position on the stage then why don't you use:
stage.mouseX,
stage.mouseY
Also, if you scale something up (let's say to a total size of 200%), then 50 pixels across on the stage is actually 25 pixels across in the container that has been scaled.
Use this as a document class to see what I mean:
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class Main extends MovieClip
{
private var _box:MovieClip;
public function Main()
{
addEventListener(Event.ENTER_FRAME, _move);
_box = new MovieClip();
_box.scaleX = _box.scaleY = 2;
addChild(_box);
}
private function _move(e:Event):void
{
trace("stage: " + stage.mouseX + ", " + stage.mouseY);
trace("box: " + _box.mouseX + ", " + _box.mouseY);
}
}
}
If you are still looking for this maybe you can use startDrag(); and stopDrag();
for dragging images.
like this:
image.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownMC);
image.addEventListener(MouseEvent.MOUSE_UP, onMouseUpMC);
function onMouseDownMC(e:MouseEvent):void
{
e.currentTarget.startDrag(true);
}
function onMouseUpMC(e:MouseEvent):void
{
e.currentTarget.stopDrag();
}
Update : You can set ur function like this:
image.addEventListener(MouseEvent.MOUSE_DOWN, onMouseMC);
image.addEventListener(MouseEvent.MOUSE_UP, onMouseMC);
function onMouseMC(e:MouseEvent):void
{
var type = e.type;
if(type == MouseEvent.MOUSE_DOWN) /// u can use "mouseDown" accept MouseEvent.MOUSE_DOWN
{
e.currentTarget.startDrag(true);// if you set true its gonna drag the obj from center
}
else if(type == MouseEvent.MOUSE_UP)
{
e.currentTarget.stopDrag(); //u can use "mouseUp" accept MouseEvent.MOUSE_UP
}
}