Coordinates don't change dragging scaled fxg graphics - actionscript-3

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());

Related

ActionScript classes reference

I have a class Square and a class Circle.
This my class Circle:
public class Circle extends MovieClip
{
var growthRate:Number = 2;
public function Circle()
{
addEventListener(Event.ENTER_FRAME, grow);
}
function grow(e :Event):void
{
e.target.width +=growthRate;
e.target.height +=growthRate;
}
}
I need to stop growing the circle inside a function from Shape.
public function Square() {
buttonMode = true;
addEventListener(MouseEvent.MOUSE_DOWN, down);
}
protected function down ( event: MouseEvent):void
{
//here i need to stop the circle
}
I don't know how to make a relation with the Circle class in order to stop the circle growing.
Thank you in advance.
I don't know how to make a relation with the Circle class in order to stop the circle growing.
That's because you cannot with the code you have right now. There's nothing in your class that's accessible from outside (public), that stops the growth. But there's not even something private in your class that does this. The functionality simply is not there.
So first of all, create the desired functionality. and make it available to public.
Here's how your Circle class could look like:
public class Circle extends Sprite
{
private var growthRate:Number = 2;
public function Circle()
{
// nothing here
// this is just to create a circle graphic, if you have artwork in your library symbol, you do not need this
graphics.beginFill(0xffffff * Math.random());
graphics.drawCircle(0, 0, 10 + 30 * Math.random());
graphics.endFill();
}
public function startGrowing(rate:Number = 0):void
{
if(rate != 0)
{
growthRate = rate;
}
addEventListener(Event.ENTER_FRAME, grow);
}
public function stopGrowing():void
{
removeEventListener(Event.ENTER_FRAME, grow);
}
private function grow(e:Event):void
{
width += growthRate;
height += growthRate;
}
}
Pay attention to
the constructor: I create a circle graphic there with code. As the comment says, if Circle is a class associated to a library symbol, you do not need this, because you already created the artwork in the symbol.
the super class: It's Sprite. This should be your default superclass. The only real reason to use MovieClip is if you have a timeline animation. It doesn't look like you have any of that from what you posted, so I recommend Sprite.
the two new public methods: startGrowing and stopGrowing, which do exactly what their names imply. startGrowing has an optional parameter to to start growing at a different growth rate.
the lack of e.target: which is unnecessary here.
A simple demo of that code looks like this:
var circle:Circle = new Circle();
circle.x = 200;
circle.y = 200;
addChild(circle);
circle.startGrowing();
//circle.startGrowing(1); // grow slowly
//circle.startGrowing(5); // grow fast
To stop the growth, stop listening for the ENTER_FRAME Event.
So far so good, now to your actual question:
how to make a relation with the Circle class
protected function down ( event: MouseEvent):void
{
//here i need to stop the circle
}
You think that you should make this connection in your Square class, but you are wrong about that. It's very bad practice to connect two classes this way. You want the classes to be as individual as possible.
Think about it like phones. Does your phone have a direct way to a specific other phone? No. It has the ability to connect to any phone, which makes it a lot more universally useful than a phone hard wired to another phone.
You make the connection outside both classes with events. That's like your phone making a call to the network with a number it wants to call. The network then figures out how to find the other phone with that number and how to establish the connection.
As a short interlude and so that we are on the same page about it, here's the Square class that I'm using:
public class Square extends Sprite
{
public function Square()
{
// nothing here
// this is just to create a circle graphic, if you have artwork in your library symbol, you do not need this
graphics.beginFill(0xffffff * Math.random());
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
}
}
As you can see, it only has a constructor in which I programmatically draw a rectangle. Again, if you have the desired artwork in your library symbol, there's no need for this. In that case, the constructor would be empty and in turn the entire class file would be empty. In this case, you do not even need a class file. Just associate the library symbol with the name. The Square is only a graphic asset without any code attached to it.
Here's a full fledged document class using both classes:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Main extends Sprite
{
private var circle:Circle;
public function Main()
{
circle = new Circle();
circle.x = 200;
circle.y = 200;
addChild(circle);
circle.startGrowing(1);
var square:Square = new Square();
addChild(square);
square.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private function onMouseDown(e:MouseEvent):void
{
circle.stopGrowing();
}
}
}
As you can see, the event listener is added in the document class and also the function that is executed when the event occurs is in Main.
Here's a variation of that without the square. This time you have to click on the circle to stop it growing:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Main extends Sprite
{
private var circle:Circle;
public function Main()
{
circle = new Circle();
circle.x = 200;
circle.y = 200;
addChild(circle);
circle.startGrowing(1);
circle.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private function onMouseDown(e:MouseEvent):void
{
circle.stopGrowing();
}
}
}
As you can see, making the connection outside both classes with events gives you a lot of flexibility to wire things up in a different way. Just like having a phone that connects to a network instead of another phone directly.

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.

how do I access the main class's stage? + Can I pass functions as arguments like this?

There are two files in my actionscript project named "TestAPP", TestAPP.as and Draggable.as
TestAPP.as:
package {
import flash.display.Sprite;
import flash.display.Stage;
public class TestAPP extends Sprite
{
var _mainStage:Stage;
public function TestAPP()//This is where we test the UI components.
{
var sp:Sprite = new Sprite();
_mainStage = stage;
_mainStage.addChild(sp);
sp.graphics.beginFill(0x00FF00);
sp.graphics.drawCircle(0,0,10);
sp.graphics.endFill();
sp.x = 50;
sp.y = 50;
var draggable1:Draggable = new draggable(sp,_mainStage,limitingfunc);
}
public function limitingfunc(x:Number,y:Number):int{
return 0;
}
}
}
And for the draggable.as:
package
{
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.MouseEvent;
public class Draggable
{
private var _limitingFunc:Function;
private var _which:Sprite;
private var _MouseSavedX:Number;
private var _MouseSavedY:Number;
private var _stage:Stage;
public function Draggable(which:Sprite,stage:Stage,limitingfunc:Function)
{
_limitingFunc = limitingfunc;
_which = which;
_stage = stage;
_which.addEventListener(MouseEvent.MOUSE_DOWN,begin_drag);
}
//limiting func: returns 0 when the object is free to move that place.
//returns -1 when the user wants to block X coordinate changes (but maintain Y free)
//returns -2 when the user wants to block Y ...
//returns -3 or less when the user wants to block both X and Y from changing.
//returns
private function Return_0(x:Number = 0,y:Number = 0):int{
return 0;
}
private function begin_drag(ev:MouseEvent):void{
var xTo:Number = _stage.mouseX - _MouseSavedX + _which.x;
var yTo:Number = _stage.mouseY - _MouseSavedY + _which.y;
var limitingFuncReturnValue:int = _limitingFunc(xTo,yTo);
if(limitingFuncReturnValue == 0){//free to move.
_which.x = xTo;
_which.y = yTo;
}
else if(limitingFuncReturnValue == -1){//free to move Y
_which.y = yTo;
}
else if(limitingFuncReturnValue == -2){
_which.y = yTo;
}
//else:do nothing.
}
}
}
In "my actionscript theory", I'm supposed to see a circle that follows the mouse when I click it. (The draggable is not fully implemented) But the circle doesn't even budge :(
...I've been trying to figure out how to access the main class's stage property. I've googled for it, but still no progress.
Please help this helpless newb!!! I'll really appreciate your help:)
Thank you!
You're implementing your 2nd class as "draggable", when you named it (and it has to be named) "Draggable" with an upper case. Change it and see if that works. You seem to be passing in the parent classes stage correctly though.
If TestAPP is your document class. You can access to the stage thru the stage property (like your are doing in your example).
If TestAPP is not the document class, you should listen first to the ADDED_TO_STAGE event and then access to the stage property.
There is no need to add _mainStage property because you already have the stage property.
In order to move objects around, you need to use the ENTER_FRAME event.
You can access the main class' stage property the same way you do it for any DisplayObject on the stage: Use this.stage.
So just this should be enough:
var sp:Sprite = new Sprite();
stage.addChild(sp);
You could then pass the sprite and the main class' stage as a parameter, the way you did - but I would recommend creating a subclass Draggable extends Sprite and instantiate the whole thing using new Draggable() instead.
The new object will automatically have its own reference to stage as soon as it is added to the display list, and limitingFunc could be a member of Draggable, as well.
Also, you can use the startDrag() and stopDrag() methods, no need to implement your own: You can specify a Rectangle as a parameter to startDrag() to limit the permitted movement.
if you need it so much you may:
private static var _instance: TestAPP;
//...
public function TestAPP(){
_instance = this;
//...
}
public static function get instance():TestAPP{
return _instance;
}
public static function set instance(newVal: TestAPP):void{
_instance = newVal;
}
and you'll be able to reference its stage from any class where your TestAPP will be imported just with TestAPP.instance.stage.
but _instance is a property of the class TestAPP itself, not its instances

Actionscript: How do I rotate an external png image using a matrix?

Okay so I have two import pieces of code involved in this. This first tiny bit is what creates an object called OBJECT_arrow. It is located in the main function of my main class:
new OBJECT_arrow().CREATE(this,200,200);
It isn't really all that important. Now this next bit is the OBJECT_arrow class. What it does is loads an external png image and draws it.
package
{
import flash.net.URLRequest;
import flash.display.*;
import flash.system.*;
import flash.events.*;
import Math;
public class OBJECT_arrow extends Sprite
{
public var X:Number = 0; public var Y:Number = 0;
public var DEPTH:int = 0 ;
public var CONTAINER:Sprite = new Sprite();
public var imageLoader:Loader = new Loader();
public var image:URLRequest = new URLRequest ('ARROW.png');
public function CREATE(CONTAINER:Sprite,X:Number,Y:Number):void
{
this.X = X; imageLoader.x = this.X;
this.Y = Y; imageLoader.y = this.Y;
this.CONTAINER = CONTAINER;
CONTAINER.stage.addEventListener(Event.ENTER_FRAME,STEP);
imageLoader.load(image);
DRAW();
}
public function STEP(event:Event):void
{
DRAW();
}
public function DRAW():void
{
addChild (imageLoader);
(CONTAINER as MAIN).DRAW_LIST[(CONTAINER as MAIN).DRAW_LIST.length] = this;
(CONTAINER as MAIN).DRAW_LIST[(CONTAINER as MAIN).DRAW_LIST.length] = DEPTH;
}
}
}
Now I know the mathematics behind rotation and know to rotate before I translate and everything but I simply don't know how to apply the transformation to an external image in as3.
When you load an image with Loader it is stored as an object of type DisplayObject.
If you want it to be rotated, just set the rotation property.
To apply a matrix, you can use the transform() method of the DisplayObject.
You should also take a look at the BitmapData (raw image data) and Bitmap (DisplayObject to hold the BitmapData) classes. Depending on the complexity of what you're trying to do, they may serve you better. Specifically, BitmapData will allow you to lock() the image while you are fiddling with its bits. Flash won't render the BitmapData until you unlock() it, which can be a great performance improvement if you're doing a lot of fiddling.

I have a AS3 class which extends movieClip with a dynamic image, how to make this image draggable?

I need to make an item draggable (dragable?) Sorry if my terminology is not right!
I have a class where I will store variable and do calculations:
package Classes
{
import flash.display.*;
import flash.events.*;
import flash.net.*;
public class PrintItem extends MovieClip
{
public var imageLoader:Loader;
public function PrintItem()
{
}
public function loadImage(url:String):void
{
imageLoader = new Loader();
imageLoader.load(new URLRequest(url));
}
}
}
I create a new instance of the class, I pass it an URL in loadImage and I add it to the canvas like this:
var myItem:PrintItem = new PrintItem();
myItem.loadImage("pic.jpg");
this.addChild(myItem.imageLoader);
myItem.imageLoader.x = 50;
myItem.imageLoader.y = 50;
myItem.imageLoader.addEventListener(MouseEvent.MOUSE_DOWN, pickUp);
myItem.imageLoader.addEventListener(MouseEvent.MOUSE_UP, dropIt);
But there I am stuck. I need to make this item draggable and write the pickUp and dropIt functions. event.target.startDrag(false); doesn't work (Loader isn't draggable) and event.target.parent.startDrag(false); doesn't work as the whole stage becomes draggable! HELP! Is it because only the Loader is added to the stage?
Ok. I know it has only been a few minutes, but I have found the answer. Writing it down here cleared it in my head. Here's what worked for me in case it helps anybody else:
First, I added the Loader image as a child to the class like this:
public function loadImage(url:String):void
{
imageLoader = new Loader();
imageLoader.load(new URLRequest(url));
this.addChild(imageLoader); // IMPORTANT!
this.mouseChildren = false;
this.addEventListener(MouseEvent.MOUSE_DOWN, pickUp);
this.addEventListener(MouseEvent.MOUSE_UP, dropIt);
}
And then I only had to add the class to the stage, not the loader:
this.addChild(myItem);
Back to the class, I added this:
public function pickUp(event:MouseEvent):void
{
trace("Pickup");
this.startDrag(false);
}
public function dropIt(event:MouseEvent):void
{
this.stopDrag();
trace("X = " + event.target.x + " Y = " + event.target.y); // Just to help me
}
Et voila! It worked!