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

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.

Related

<a> link to nearest object of a defined class

So I would like to have a link that would point to the nearest element with the class ".example" on their screen. I know you can have links pointing to unique IDs on the page setting the href to #uniqueID I just want to know if that is possible with classes
You'd need to use JavaScript for this. Assuming that by 'nearest element... on their screen' you mean the element with the smallest vertical distance to the clicked link.
A quick way to do this would be looping through each element with the class example, finding the one with the smallest distance, and scrolling it into view:
<script>
function toNearestExample(clicked_element) {
// first we get all elements of 'example' class
var examples = document.getElementsByClassName("example");
// now we find the index of the element nearest to the clicked element
// to do this we go through each element in this list, and find the closest vertical distance from the clicked element
var smallest = Math.abs(clicked_element.getBoundingClientRect().top - examples[0].getBoundingClientRect().top);
var indexOfSmallest = 0;
for(var i=1;i<examples.length;i++) { // for each element with class 'example'
// calculate vertical distance
var verticalDistance = Math.abs(clicked_element.getBoundingClientRect().top - examples[i].getBoundingClientRect().top);
if (verticalDistance < smallest) {
smallest = verticalDistance;
indexOfSmallest = i;
}
}
// once we have that, we can scroll it into view
examples[indexOfSmallest].scrollIntoView();
}
</script>
Now all we have to do is call this function when your element is clicked, making sure to pass the element that clicked it:
<a onclick="toNearestExample(this);">click me!</a>
Of course, you could easily modify the code for elements with distances other than vertical.
An issue with this method is that it will get slow as you add more and more example elements onto the page. Unless your <a> tag will be moving around the page, it would be better to just hardcode this in for performance or to prevent issues with different window sizes.

Save un-scaled canvas with image background after changes were applied

I've got a big issue and it's almost a week trying to make it work so any help I would really appreciate - I am trying to create a simple image editor in html5, so I upload an image, load it into canvas and then paint on it -
I also want to be able to zoom in and zoom out- just that I can't figure out how should I save the canvas state - for the paint mouseevents I am using an array which saves canvas.toDataUrl, but this one will save only what it is visible in canvas, only a part of the scaled image, and not the entire one -
if anyone knows how can I un-scale the canvas together with the painting over it and save it in the stack from where I can retrieve it for other painting events, I'll appreciate a lot! Thanks
Saving state
The canvas' save() and restore() is not related to the pixels in the canvas at all. Save() only saves current pen color, fill color, transform, scale, rotation and so forth - parameter values only, not actual pixel data.
And so, the restore() will only restore these parameter values to the previous ones.
The canvas element is passive, meaning it only holds the pixels that you see on the screen. It does not keep a backup of anything so if you change its size, re-size browser window or open dialogs in the browser causing it to clear, you will need to update the canvas yourself.
This also applies when you change a parameter value such as scale. Nothing on the canvas will change setting a new value. The only thing that happens is that your next draw of what-ever will use these parameter values for the drawing (in other words: if you apply rotation nothing rotates, but the next thing you draw will be rotated).
Drawing on existing image
As you need to maintain the content it also means you need to store the image you draw on as well as what you draw.
When you draw for example lines you need to record every stroke to arrays. When the canvas needs an update (ie. zoom) you redraw the original image first at the new scale, then iterate through the arrays with lines and re-render them too.
Same for points, rectangles, circles and what have you..
Think of canvas as just a snapshot of what you have stored elsewhere (image object, arrays, objects) . Canvas is just a view-port for that data.
I would recommend to store as this:
var backgroundImage; //reference to your uploaded image
var renderStack = []; //stores all drawing objects (see below)
//example generic object to hold strokes, shapes etc.
function renderObject() {
this.type = 'stroke'; //or rectangle, or circle, or dot, ...
this.x1;
this.y1;
this.x2;
this.y2;
this.radius;
this.penWidth;
this.penColor;
this.fillColor;
this.points = [];
//... extend as you need or use separate object for each type
}
When you then draw a stroke (pseudo):
var currentRenderObject;
function mouseDown(e) {
//get a new render object for new shape/line etc.
currentRenderObject = new renderObject();
//get type from your selected tool
currentRenderObject.type = 'stroke'; //for example
//do the normal draw operations, mouse position etc.
x =..., y = ...
}
function mouseMove(e) {
//get mouse positions, draw as normal
x = ..., y = ...
//store the points to the array, here:
//we have a line or stroke, so we push the
//values to ourpoint-array in the renderObject
currentRenderObject.points.push(x);
currentRenderObject.points.push(y);
}
function mouseUp(e) {
//when paint is done, push the current renderObject
//to our render stack
renderStack.push(currentRenderObject);
}
Now you can make a redraw function:
function redraw() {
clearCanvas();
drawBackgroundImage();
for(var i = 0, ro; ro = renderStack[i]; i++) {
switch(ro.type) {
case 'stroke':
//... parse through point list
break;
case 'rectangle':
//... draw rectangle
break;
...
}
}
}
function zoom(factor) {
//set new zoom, position (scale/translate if you don't
//want to do it manually). Remember that mouse coords need
//to be recalculated as well to match the zoom factor.
redraw();
}
//when canvas is scaled for some reason, or the window
canvas.onresize = windows.onresize = redraw;
A bonus doing this here is you can use your render stack as a undo/redo stack as well...
Hope this helped to better understand how canvas works.

Changing levels of nested movieclips in Flash AS 3.0

I am working on a zooming square navigation project (visually like Windows Metro, but squares take over the screen when clicked on). I'm having trouble getting the squares to the top layer (setChildIndex). Here is the code for when a square is clicked on:
function zoom(event:MouseEvent):void {
// saves location and size of icons
returnto = event.currentTarget;
returntoX = event.currentTarget.x;
returntoY = event.currentTarget.y;
returntoWidth = event.currentTarget.width;
returntoHeight = event.currentTarget.height;
// turn off button behaviours
event.currentTarget.buttonMode = false;
event.currentTarget.alpha = 1;
event.currentTarget.removeEventListener(MouseEvent.MOUSE_OVER, rollover);
event.currentTarget.removeEventListener(MouseEvent.CLICK, zoom);
event.currentTarget.removeEventListener(MouseEvent.ROLL_OUT, rollout);
// put clicked box on top
setChildIndex(returnto, numChildren-1);
// fade out icon decor
TweenLite.to(returnto.decor, .25, {alpha:0});
// make square take over screen, call finished when done
TweenLite.to(returnto, 1, {x:stage.x,y:stage.y, width:stage.stageWidth, height:stage.stageHeight, ease:Expo.easeInOut, onComplete:finished});
}
the squares are all inside a main movieclip (named navigation) on the stage (so the first square is navigation.sq_1)
I am not a Flash developer, so I'm really struggling with AS3. Hopefully this is enough info for someone to help. Thanks!
S
Just try to add your returnto clip with addChild() method:
// put clicked box on top
addChild(returnto);
Display List's default behavior when addChild() is to put the child on top of all childs. Other children's indexes are recounted.

Take glow from a DisplayObject and put it in another

I have a grid of DisplayObjects that when I put a glow on any of them, the glow overlap the other objects.
I was wondering if I could take the glow created by a DisplayObject and put it another, so I can attach it to a container that's behind all the objects? If someone can suggest a better idea, that would be much appreciated too.
Kodiak has the right idea here but it sounds like once you add the glow to the object it stays there and you don't remove it. (For example you don't do somethings like on mouse hover -> glow, on mouse out -> no glow) You want something like Once I hover over the object it glows and the glow itself is always behind the other display objects.
This seems odd to me but what you could is create a duplicate of the grid and place it underneath of the top level ones and then do somethings like...
public function test() {
// constructor code
primaryDisObj:Array() = new Array(primaryDO1, primaryDO2);// an array of the instances of the Display Objects on top
secondaryDisObj:Array() = new Array(secondDO1, secondDO2);// an array of the instances of the Display Objects underneath the originals
for(var i:uint = 0; i < primaryDisObj.length; i++){
primaryDisObj[i].name = i;
primaryDisObj[i].addEventListener(MouseEvent.MOUSE_OVER, overPrimary);
}
}
private function overPrimary(e:MouseEvent):void{
secondaryDisObj[e.target.name as uint).filters = [new GlowFilter(0xFF6699, .75, 25, 25, 2, 2, false, false)];//Or however you want you filer to look
}
Without seeing you code it's kind of hard to figure out what you're going for but this is the simplest way I can think of to do this.
Can't you just change the index of the glowing object to put it in the back?
myItem.parent.setChildIndex(myItem, 0);

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

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'