Is there a function or property like css-zIndex in actionscript? - actionscript-3

I want to control which DisplayObject is displayed on the front or on the back.
I can control that with zIndex style in css.
How can I do that in as?

Look at setChildIndex()
Here's a quick function that will let you place something at the highest depth:
function toTop(child:DisplayObject):void
{
if(child.parent)
{
child.setChildIndex(
child.parent.numChildren - 1
);
}
}

To control the z-order of the displayObjects use setChildIndex().
Lowest indexed children are displayed at the bottom.
Highest indexed children are displayed at the top.
To change the zorder of the children, use setChildIndex, to illustrate:
var container:Sprite = new Sprite();
var child1:Sprite = new Sprite();
var child2:Sprite = new Sprite();
var child3:Sprite = new Sprite();
container.addChild(child1); // bottom
container.addChild(child2); // middle
container.addChild(child3); // top
container.setChildIndex(child3,0); // child3 would now be at the bottom

There are even more options than only setting the 'z-index', you have full control on the layers:
getChildAt(index:int):DisplayObject
Gets an object from certain index
getChildIndex(child:DisplayObject):int
Gets a index from certain object
addChild(child:DisplayObject):DisplayObject
addChildAt(child:DisplayObject, index:int):DisplayObject
Adds a object on top (addChild) or at certain index (addChildAt)
swapChildren(child1:DisplayObject, child2:DisplayObject):DisplayObject
swapChildrenAt(index1:int, index2:int):void
Swap objects with eachother of by its 'z-index'

Related

How to remove child with same name if in same location or collision? AS3

So, in my script I have given the user the ability to make unlimited amount of a certain movieclip.
var square = new Square();
sqaure.x = mouseX;
sqaure.y = mouseY;
addChild(square);
However, I would like to have the script remove any extra children added to the same X and Y coordinates. I need to make sure it removes the extra child even if they click and move the cursor away and then click back to an already populated location later. Either in the .class file or in the main script itself.
Any ideas? Thanks
At the moment of click you can obtain a list of all the things under the mouse cursor with the getObjectsUnderPoint(...) method and remove any subset of them upon criteria of your liking.
// Stage, because if user clicks the current container
// into the empty area, the click won't register.
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
function onDown(e:MouseEvent):void
{
var aPoint:Point = new Point;
// Set it to the mouse coordinates.
aPoint.x = mouseX;
aPoint.y = mouseY;
// Convert it to Stage coordinates.
aPoint = localToGlobal(aPoint);
// The returned list will contain only descendants
// of the current DisplayObjectContainer.
var aList:Array = getObjectsUnderPoint(aPoint);
// Iterate through the results.
for each (var aChild:DiaplayObject in aList)
{
// Now, filter the results to match certain criteria.
// Don't bother with the grandchildren.
if (aChild.parent != this) continue;
// Ignore things if they are not of the right class.
if (!(aChild is Square)) continue;
// ...etc.
// Remove those ones that have passed all the checks.
removeChild(aChild);
}
// Add the new one here.
var aSq:Square = new Square;
aSq.x = mouseX;
aSq.y = mouseY;
addChild(aSq);
}
One thing that Organis said, the "addEventListener" is something you can take a look at using the search terms "as3 event listener api". the "api" searches will come up with adobe specific code and property examples.
You can try to put in small input text boxes and a button with an event listener to set x and y to the values of the input text boxes
Another thing, I've always done it best with arrays to hold every item that you are adding to the stage.
//global variables
var nameSprite:Sprite;
var name2Array:Array = new Array();
var id:Number = 0;
//initial function
nameSprite = new Sprite();
addChild(nameSprite);
name2Array = new Array();//seems redundant but has been what I've had to do to make it work
//other function to add items to the array
var sqaure:objectName = new objectName();
sqaure.x = mouseX;
sqaure.y = mouseY;
square.id = id;//I like to add an id to be able to better sort things
nameSprite.addChild(sqaure);
name2Array.push(sqaure);
id++;
//function to remove items
IndexPH = j;//j would be the index in the for loop to identify the entry to be removed
nameSprite.removeChild(name2Array[IndexPH]);//removes from stage
name2Array.splice(IndexPH,1);//removes from array
//function to sort the array after an item has been removed
name2Array.sortOn(["id"], Array.NUMERIC);
So this is a bunch of things that you can mess around with if you need ideas. I tend to search and search and then find a little bit of code to incorporate into my projects while not necessarily using every part of a specific code example.

Compositing the stage's last frame

I've created a series of classes that can be used to generate and render images. I want to store a copy of the last frame displayed so I can mix it with the current frame to create a video sustain effect. A brief overview of the classes involved in this example:
MasterContainer: a subclass of Sprite used as the main display object. Generative classes are placed in the MasterContainer, and redrawn when the container is told to render
CustomWave: a subclass of Shape used to contain, draw, and manipulate a GraphicsPath object. One of the aforementioned 'generative classes'
My current attempt involves the use of two MasterContainer objects - one for the current frame, and one for the last frame. If I'm not mistaken, the current appearance of one MasterContainer (and its children) can be copied to the other with a command like lastMaster.graphics.copyFrom(master.graphics);. Consider the following code:
var time:Number;
var master:MasterContainer = new MasterContainer(); //current frame
var lastMaster:MasterContainer = new MasterContainer(); // last frame
var wave:CustomWave = new CustomWave(new <Number>[0,0,0,0],0xffffff,5); //generator for current frame
master.RegisterComponent(wave); //adds CustomWave and registers with the rendering loop
addChild(lastMaster); //add last frame to stage
addChild(master); //add current frame to stage
addEventListener(Event.ENTER_FRAME, perFrame);
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //copy previous frame's graphics
UpdatePoints(); //update the path of the CustomWave
UpdateColor(); //update the color of the CustomWave
master.fireRenderCannon(); //redraw objects registered to master
}
This seems to work in theory, but as far as I can tell lastMaster ends up with no visible graphics content even though master renders as expected. I've tried several times to test whether this is the case, and am pretty convinced that that it is, but am newish to AS3 and am concerned I am overlooking something - the code looks like it should work. Does anyone have suggestions on how to test this properly? Are there obvious defects within this code that would cause lastMaster to be visually blank? Is there an better way of accomplishing my goal?
I think I'm in over my head on this... I would love any input. Thanks!
After you copied graphics, what do you try to do with it?
Method copyFrom works as clocks, without any problems. Isn't here logic bug in your code?
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //Here
//master.graphics.copyFrom(lastMaster.graphics);
UpdatePoints();
UpdateColor();
master.fireRenderCannon();
}
Example of copyFrom, it works fine with any complexity of graphics:
var complex: Shape = new Shape();
adobeExample(complex.graphics);
var test2: Shape = new Shape();
test2.graphics.copyFrom(complex.graphics);
addChild(test2);
private function adobeExample(graphics: Graphics):void{
// define the line style
graphics.lineStyle(2,0x000000);
// define the fill
graphics.beginFill(0x666699);//set the color
// establish a new Vector object for the commands parameter
var star_commands:Vector.<int> = new Vector.<int>();
// use the Vector array push() method to add moveTo() and lineTo() values
// 1 moveTo command followed by 3 lineTo commands
star_commands.push(1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2);
// establish a new Vector object for the data parameter
var star_coord:Vector.<Number> = new Vector.<Number>();
// use the Vector array push() method to add a set of coordinate pairs
star_coord.push(0,0, 75,50, 100,0, 125,50, 200,0, 150,75, 200,100, 150,125, 200,200, 125,150, 100,200, 75,150, 0,200, 50,125, 0,100, 50,75, 0,0);
graphics.drawPath(star_commands, star_coord);
}
After the comments made by Bennet and Nicolas, it became obvious that my requirements were (nearly) impossible without a fair amount of redesign. The changes made are as follows:
Generators are no longer DisplayObjects. They are only used to calculate vectors containing the IGraphicsData objects necessary to draw the generated graphic with the drawGraphicsData method.
MasterContainer is now a shape subclass that retrieves the Vector.<IGraphicsData> from each registered generator in order to draw the output.
A bitmap subclass is used to render the contents of the MasterContainer, combining it with a color-dampened version of the previous frame.
An abridged version of the bitmap subclass:
private var constantSustain:Number;
private var linearSustain:Number;
private var sustain:ColorTransform;
private var lastFrame:BitmapData;
public function BitmapManipulator(constantSustain:Number = 0.998, linearSustain:Number = 0.98) {
this.constantSustain = Math.min(Math.max(constantSustain, 0), 1);
this.linearSustain = Math.min(Math.max(linearSustain, 0), 1);
this.UpdateSustain();
this.addEventListener(Event.ADDED_TO_STAGE, OnAddedToStage)
}
private function UpdateSustain():void {
var constantRelease:Number = 255 * (this.constantSustain - 1);
this.sustain = new ColorTransform(this.linearSustain, this.linearSustain, this.linearSustain, 1,
constantRelease, constantRelease, constantRelease, 0);
}
private function OnAddedToStage(event:Event) {
this.lastFrame = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0);
}
public function DrawFrame(container:MasterContainer):void {
this.lastFrame.draw(container);
this.bitmapData = lastFrame;
this.lastFrame = this.bitmapData
this.lastFrame.colorTransform(getBounds(this), this.sustain);
}
...and finally the results #60fps when using an indigo sine wave of shifting phase as the input for the CustomWave:

Get BitmapData from a displayObject included transparent area, and effect area

I have this function:
public static function cloneDpObj(target:DisplayObject):Bitmap
{
var duplicate:Bitmap;
var tBitData:BitmapData = new BitmapData(target.width, target.height);
tBitData.draw(target);
duplicate = new Bitmap(tBitData);
return duplicate;
}
to clone target displayObject (MovieClip or Sprite) and return Bitmap Object.
It can get bitmap from the target object, but it seem don't get all the area of the image.
By give the width and height of target object, but the target object in design was applied by Glow Effect, so my question can we get the all view of bitmapdata from a displayobject?
BitmapData.draw() takes a snapshot of a given object removing all transformations and filters applied on the stage. Resulting image shows object as it is present in your movie library.
There are two basic options when drawing display objects with transformations and/or filters.
You can apply all transformations during drawing with matrix parameter for BitmapData.draw(). After drawing you can apply filters to resulting bitmap with BitmapData.applyFilter().
Just draw parent container, not the object itself.
I usually choose the latter. That's pretty straightforward. There are some disadvantages: if you choose the second method, your target has to have a display list parent and you may draw unwanted content that resides in parent container. (However, these drawbacks are easily eliminated.)
// bounds and size of parent in its own coordinate space
var rect:Rectangle = target.parent.getBounds(target.parent);
var bmp:BitmapData = new BitmapData(rect.width, rect.height, true, 0);
// offset for drawing
var matrix:Matrix = new Matrix();
matrix.translate(-rect.x, -rect.y);
// Note: we are drawing parent object, not target itself:
// this allows to save all transformations and filters of target
bmp.draw(target.parent, matrix);
You need compute the area/rectangle of your DisplayObject including the area taken by the filter applied. Luckily you can do that with with built-in functionality by using the generateFilterRect() method of the BitmapData class.
Also, for other reasons, if you need the transformation of your DisplayObject applied to the BitmapData snapshot, you can pass the source DisplayObject's .transform.concatenatedMatrix as the second parameter of BitmapData's draw() method.
Thank you very much to all of you that take valuable time answer my question. I improved that function, but I is better, but I notice that the width of result of capture is 1pixel offset, so I decided to add 1 pixel to width of the bitmapdata, I know that is not a good practice. because I have to do that now, I don't know the issue yet. Here is how our function now:
public static function cloneDpObj(target:DisplayObject, optWidth:Number = -1, optHeight:Number = -1):Bitmap
{
var duplicate:Bitmap;
if (!target.parent) {
var tempSprite:Sprite = new Sprite;
tempSprite.addChild(target);
}
var rect:Rectangle = target.parent.getBounds(target.parent);
var bmp:BitmapData = new BitmapData(rect.width + 1, rect.height, true, 0);
// offset for drawing
var matrix:Matrix = new Matrix();
matrix.translate( -rect.x, -rect.y);
// Note: we are drawing parent object, not target itself:
// this allows to save all transformations and filters of target
bmp.draw(target.parent, matrix);
duplicate = new Bitmap(bmp);
return duplicate;
}
I would actually go with Nox's first option as the easier approach, and modifying your function to do it should only take one extra line of code:
public static function cloneDpObj(target:DisplayObject):Bitmap
{
var duplicate:Bitmap;
var tBitData:BitmapData = new BitmapData(target.width, target.height);
tBitData.draw(target);
duplicate = new Bitmap(tBitData);
//add the filters
duplicate.filters = target.filters;
return duplicate;
}

How can I add scrollbars to a SkinnableContainer created runtime?

I created a SkinnableContainer runtime because I want to set some style properties to it, then based on some external data I need to create a new SkinnableContainer and add it as children of the first one. This children height can be greater than the parent container.
How can I create a SkinnableContainer runtime with some scrollbars?
I read in the documentation that I need to create a new Skin.
It is possible runtime to achieve the same result ?
// ... in a container ...
var father = new SkinnableContainer();
this.addElement(father);
var child = new SkinnableContainer();
// ... some initialization... child is filled with some other elements from outside
father.addElement(child);
// ... now if child.height > father.height
// I want to add a vertical scrollbar
You could always put the child in a Scroller control.
For example:
var father = new SkinnableContainer();
this.addElement(father);
var scroller = new Scroller();
var child = new SkinnableContainer();
// ... some initialization... child is filled with some other elements from outside
// scroller.addElement(child); // wrong because you cannot add element to a scroller
scroller.viewport = child.contentGroup; // but you can set this to an IViewport
father.addElement(scroller);
father.addElement(child);

Is there a way to keep an object always at the top of the display list?

Is there a way to make a display object always be at the top of the display list?
For example, I know you can set the childIndex, i.e:
setChildIndex(myDisplayObject, numChildren-1);
But is there a way that an object has sensed that something else has been added to the display list and restack themselves accordingly?
You can listen to the Event.ADDED event on the container. This event will bubble up, so you'll get called whenever a display object is added to the container or any of its children.
Here's an example of how you could do this. You'll see the black box always stays on top.
var container:DisplayObjectContainer = this; // this is a frame script but just change this line as necessary
container.addEventListener(Event.ADDED,handleAdded,true);
function handleAdded(e:Event):void {
// this if tries to cover some edge cases, unlikely to happen in your code, from your description
if(container == topElement.parent && container.numChildren > 0 && container.getChildIndex(topElement) != container.numChildren - 1) {
container.setChildIndex(topElement,numChildren - 1);
}
}
function getSprite(c:uint):Sprite {
var sp:Sprite = new Sprite();
sp.graphics.beginFill(c);
sp.graphics.drawRect(0,0,100,100);
sp.graphics.endFill();
return sp;
}
var topElement:Sprite = getSprite(0);
container.addChild(topElement);
var sp:Sprite = getSprite(0xff0000);
container.addChild(sp);
sp.addChild(getSprite(0xff00));
var sp2:Sprite = getSprite(0xff);
container.addChild(sp2);
However, I think it's much simpler and cleaner just to have 2 containers, say, top and bottom, kind of like layers. In top you'd add the element that always must be on top (or this could be your element as you probably don't need to have this extra container). In bottom you'd add and remove whatever you want. Then you can forget about manually restacking stuff (at least to keep the top element atop).
You can override the addChild() method in the parent object. In that method you can move your child to the top using
setChildIndex(myDisplayObject, numChildren-1);
In this way everytime an object is added to the parent, the parent moves your object to the top.