I have a table (just drawn onto the stage) which has some values in it. You should be able to click on the different boxes in the table, and hide or show the value in that box.
I did this by having a layer on top of the layer with the table that has white buttons (same color as background) on it. So you should be able to click on the button, make it invisible to show the value, then click again to hide the value.
import flash.events.MouseEvent;
//these are each of the instances of the white buttons.
var visibleValues:Array=new Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
for(var t:int=0; t<visibleValues.length; t++){
visiblesValues[t].addEventListener(MouseEvent.CLICK, showValue);
}
function showValue(evt:Event):void{
if (evt.target.visible){
evt.target.visible=false;
}
else{
evt.target.visible=true;
}
}
Clicking on the button when it is visible will make it invisible (showing the number underneath), however when I try to click on it again the button does not reappear.
Why is this? Also out of curiosity is there a simpler way to do this? It seems odd to have to assign the event listener to each instance, when they are all instances of the same thing.
Thanks very much!
For your first problem try setting the alpha to 0.1 instead of visible to false. When you set visible to false you are also disabling user interaction.
Click events should bubble so you can add the listener once to the parent container then use something like the event.currentTarget to get access to the button actually clicked. Where this becomes a little harder is when the label or skin parts of the button are assigned to the currentTarget.
To get around that you can either set mouseChildren to false on each button or in the event handler keep calling .parent until you hit a button or the parent container.
Note that when you listen higher you will also get clicks on the parent so you will need to handle that case in the handler.
function showValue(evt:Event):void {
// Detect which button was clicked
var target:DisplayObject = evt.currentTarget as DisplayObject;
while(target) {
if (target is Button) {
break;
}
// container clicked so exit
if (target == container) {
return;
}
target = target.parent;
}
// No button was clicked so exit
if (!target) {
return;
}
// If we are solid then hide
if (target.alpha == 1){
target.alpha = 0.1;
} else{
target.alpha = 1;
}
}
Related
In Adobe Animate (HTML5 Canvas) I need to check if two buttons have been clicked on and advanced to the second frame and if both have then display a movieclip.
I am new to programming and not sure how to even begin. I thought of using eventListner or an if/else statement. Not getting either to work. Any help would be greatly appreciated.
I'll try and answer the question to my knowledge, hopefully, it is simple enough to follow, but I'm sure there are probably better ways to write these functions.
buttonClick(evt) allows the buttons to be toggled on and off, assuming that frame 0 of the movieclips handles the off state of the buttons and frame 1 handles the on state. A function is then called on every click to check the states of the buttons.
checkButtonStates() is a conditional statement that just checks the current frame of the buttons, and if both are at 1 the movieclip is displayed and played.
const button1 = stage.children[0].button1_mc;
const button2 = stage.children[0].button2_mc;
const movieclip3 = stage.children[0].movieclip3_mc;
button1.addEventListener("click", buttonClick);
button2.addeventListener("click", buttonClick);
function buttonClick(evt) {
button = evt.currentTarget;
// check current frame of button and changes frame
if (button.currentFrame == 0){
button.gotoAndStop(1);
} else {
button.gotoAndStop(0);
}
checkButtonStates();
}
function checkButtonStates(){
// if both buttons have been clicked hide buttons and play movie
if (button1.currentFrame == 1 && button2.currentFrame == 1){
showButtons(false);
showAndPlayMovie(true);
}
}
// buttons visible property set by passed parameter
function showButtons(bool){
button1.visible = bool;
button2.visible = bool;
}
// changes visible property of movie by passed parameter. If movie is visible the movie is played.
function showAndPlayMovie(bool){
movieclip3.visible = bool;
if (movieclip3.visible == true){
movieclip3.play();
}
}
tldr:
how does one tell a mouse event to only fire for the youngest child under the mouse, and not its parent?
I'm just making a solitaire game to try to better learn OOP. I have a Card class and a CardHome class. Each Card contains a CardHome which can hold a card, and so on. Thus, moving a card will move all the cards contained in it. The problem is that my mouse event is detecting the parent of all the cards. So what I think I need is a way to say "run this mouse event only for the youngest child of the target". Any way to do that?
private function pressCard(me:MouseEvent):void{
var c:Object = me.target as Card;
// place card in center of container x wise
c.x = 0;
// and down 5
c.y = 5;
// bring drag container up to front z order
addChild(c as Card);
// begin dragging drag container
c.startDrag(true);
// hide mouse
Mouse.hide();
// add listener to this card for mouse up
c.addEventListener(MouseEvent.MOUSE_UP,dropCard);
}
private function dropCard(me:MouseEvent):void{
var c:Object = me.target as Card;
// release drag container
c.stopDrag();
// remove mouse up listener from card
c.removeEventListener(MouseEvent.MOUSE_UP,dropCard);
Mouse.show();
// check for colision with a card that has a matching holder
for (var i:int = 0; i < deckArray.length; i++){
if (c.hitTestObject(deckArray[i]) && c != deckArray[i]){
trace("hit",deckArray[i]._containerOwned._occupied,deckArray[i]._flippable);
if (deckArray[i]._number == c._number + 1 && deckArray[i]._flippable == false && deckArray[i]._suit != c._suit && deckArray[i]._containerOwned._occupied == false){
c._containedIn.parent._containerOwned._occupied = false;
makeFlippable(c._containedIn.parent);
deckArray[i]._containerOwned.addChild(c as Card);
c._containedIn = c.parent;
c.x = 0;
c.y = 0;
break;
}
}
}
// add selected card back to home spot
c._containedIn.addChild(c);
c.x = 0;
c.y = 0;
}
private function mOver(me:MouseEvent):void{
trace(me.target._number);
trace(getHighestZ());
}
private function getHighestZ():Card{
var highestZ:int = 0;
var c:Card;
for (var i:int = 0; i < deckArray.length; i++){
if (deckArray[i].hitTestPoint(stage.mouseX,stage.mouseY) && deckArray[i].getChildIndex() > highestZ){
c = deckArray[i];
highestZ = deckArray[i].getChildIndex();
}
}
return c;
}
Each card is a child of a container in the card above it. I want to be able to select a card and drag it (and its children). The problem is that when I click card B, it selects card E. So what I need is to select the youngest child in the mouse event, and NOT its parent. Any way to control that?
I know I can do mouseChildren true or false. That's fine. It has to be set to true for all of the cards so that I can select them independent of their parent card. Do I need to do something with bubbling my event? That's something I've never understood.
Note: I realize there are problems with mouse mouse over function. Fixing that will be trivial and is kind of a placeholder for now.
tldr:
use a combination of useCapture set to true and the stopPropagation method. This allows a user to click an element and have the eventHandler only called for the target, not any of the elements between the target and the stage!
So the key here is to access the as3 event flow. When a mouse event gets triggered it propagates from the stage up through all objects that can receive that type of mouse event. In this case, the cards parents. Then it bubbles back down to the stage. So what I needed was to set the capture setting to true which switches the phase in which the event handler is used to the bubbling phase (this causes the first element to actually call the function to be the actual target) and then, instead of letting the event propagate back down to the stage, call the stopPropagation method. Looks something like this:
c.addEventListener(MouseEvent.MOUSE_OVER, mOver, true);
The true here says to fire the function on each element on the way back to the stage from the target. Then during the mOver function I do:
stopPropagation();
trace(c._Number);
// and any other code to do on this target
Which keeps the event from flowing back down through the other cards under the mouse.
Now instead of an output like
5
3
9
when the mouse is over the 9 card (which is over a 3 which is over 5) I now get simply
9
and then when I mouse over the 3 I get
3
So now the mouse is calling a function for the target only. Surprisingly non intuitive but allows for some serious flexibility and control!
from Adobe on Event Flow
I have a SWF that displays a field of stars as a single BITMAP added to the Stage. The image is dynamically generated through the Bitmap's BitmapData, to allow trackball interaction with the star field. Planets and other buttons are also on the stage with hint Labels underneath.
All works as expected. However, sometimes clicking a button, performs an operation, that does not show up until the mouse moves. For example, clicking the button to toggle planet labels, will immediately update the stage with labels turning on and off. But sometimes, the click does nothing, until the mouse is moved, or another click is made and then both clicks activate in series.
Same goes for a button that resets the star field to show either the Sun to Earth or Earth to Sun view. Click the image (button) and the background star field updates immediately. However, sometimes it does nothing until the mouse moves. It seems like an interface timing issue beyond my control.
Anybody encounter this situation? Working on MAC with Firefox, all latest updates.
Here's the event code.
private function moveRespond(e:MouseEvent):void {
var X:int=mouseX,Y:int=mouseY,R:int=Assets.Assets.radius/10; if(R<15){R=15}
if( moveActive ) {
Assets.Assets.STUFF.view.rotateMatrix(PT.x,PT.y,X,Y);
FIELD.update(w,h);
}
else {
Assets.Assets.STUFF.line.hover(X,Y,R);
var btn:Vector.<Button>=Assets.Assets.BTN_list;
var i:int,I:int=btn.length;
for( i=0; i<I; i++ ) {
btn[i].label.visible=btn[i].hover(X,Y);
} }
DP.x=PT.x-X; PT.x=X;
DP.y=PT.y-Y; PT.y=Y;
}
private function upRespond(e:MouseEvent):void { moveActive=false }
private function downRespond(e:MouseEvent):void { moveActive=true;
var X:int=mouseX,Y:int=mouseY;
PT.x=X; PT.y=Y;
}
private function clickRespond(e:MouseEvent):void {
...
//lots of button management, similar to above
//FIELD.update(w,h); and new zodiac sign fetch...
//but if there was a soft error in here,
//then the click at the end would get skipped,
//and it doesnt.
...
if( click ) { Assets.Assets.soundClick(X); }
}
Because of the randomness of the error, I'm assuming a memory thrashing somewhere in my code that is interrupting the next event, but kinda hoping its a communications issue because the missed-click is Event-queued. It just doesnt get sent to my App until either a move or another click. The App.swf can be run without the wrapper (preloader basically), and it has the same randomness. Even though it is very rare, it is still very annoying.
There is also a ConstantUpdate timer:
public function constantUpdate(evt:TimerEvent):void {
Mouse.hide(); Mouse.show();
dateMessage();
if( !Assets.Assets.BTN_help.glowing && Math.random()<0.05 ) { Assets.Assets.BTN_help.glow(); }
if( !Assets.Assets.BTN_logo.glowing && Math.random()<0.01 ) { Assets.Assets.BTN_logo.glow(); }
if( !Assets.Assets.BTN_next.glowing && Math.random()<0.05 ) { Assets.Assets.BTN_next.glow(); }
Assets.Assets.BTN_help.update();
Assets.Assets.BTN_logo.update();
Assets.Assets.BTN_next.update();
Assets.Assets.BTN_star.update();
Assets.Assets.BTN_note.update();
Assets.Assets.BTN_moon.update();
}
Just more button management. The Mouse hide/show pings the Mouse after the contextMenu deactivates. Otherwise, the mouse turned into an arrow over the contextMenu and would stay that way until mouse left the window and returned. Still have some mouse hick-ups on startup though - not setting my cursor after App.swf loaded. Have no idea if that is related to the clicking issue, but here's that code:
var cursor:MouseCursorData=new MouseCursorData();
cursor.hotSpot=new Point(
Assets.Assets.BTN_sun.icon.width/2,
Assets.Assets.BTN_sun.icon.height/2);
var pointer:Vector.<BitmapData>=new <BitmapData>[Assets.Assets.BTN_sun.icon.bitmapData];
cursor.data=pointer;
Mouse.registerCursor("myCursor",cursor);
Mouse.cursor="myCursor";
I dont keep a local handle to the cursor object.
I currently have a graphic animation with a simple play/pause button beneath, which stops and starts the entire animation:
Frame 1:
stop();
btn_2.addEventListener (MouseEvent.CLICK, stopplaying);
function stopplaying(e:MouseEvent):void {
MovieClip(root).stop();
stop();
gotoAndStop(2);
}
Frame 2:
stop();
btn_1.addEventListener (MouseEvent.CLICK, startplaying);
function startplaying(e:MouseEvent):void {
MovieClip(root).play();
play();
gotoAndStop(1);
}
This works simply and perfectly. However, I'd like the control button to show up on mouseover, and once again become transparent when the mouse leaves the area of the animation. Simply mapping alpha states to the mouse events works, but also seems to break the functionality of the button. Any help would be hugely appreciated!
Update: #BadFeelingAboutThis has good logic, but I'm not having much success with it. To be clear, frame 1 of my scene's actions is now:
var btn_1, btn_2;
this.addEventListener(MouseEvent.MOUSE_OVER, mouseOver);
this.addEventListener(MouseEvent.MOUSE_OUT, mouseOut);
function mouseOver(e:Event):void {
if(btn_1) btn_1.visible = true;
if(btn_2) btn_2.visible = true;
}
function mouseOut(e:Event):void {
if(btn_1) btn_1.visible = false;
if(btn_2) btn_2.visible = false;
}
The button is hidden, but is not reappearing on mouseover. The only fail-point I can see is the keyword 'this', that is, that I'm using it incorrectly. Let me know if there's any other info I can provide!
Update 2: Some more information (and I apologize for my dimness here): here is the animation: [link snipped, updated link below]. The play/pause button is a movie clip named "pp" that contains two frames, each with a button, one named btn_1, the other btn_2.
Update 3: I added a transparent background square (named "backpp") as a mouseevent area (instead of using the broader "this"):
backpp.addEventListener(MouseEvent.MOUSE_OVER, mouseOver);
backpp.addEventListener(MouseEvent.MOUSE_OUT, mouseOut);
This works great! When I mouseover the square, the controls show up. When I mouseout, they go away. However, the play/pause functionality is now not functioning. Any ideas?
Update 4: Most recent code/context below. The play/pause button is now sort-of functioning, and is hiding as intended, but is exhibiting a visual "flashing" behavior, as seen here: http://allaboarddesign.com/rodney/rodney-test.swf
Here is a screenshot of my FlashPro context:
You can do the following:
//create placeholder vars for your btns (they will be populated by the instances on the timeline)
//do this so the compiler knows they exist
var btn1, btn2;
//hide the buttons, do this on frame 2 as well but with btn2
btn1.visible = false;
//listen for mouse over/out on `this` (the timeline whose code this on, presumably your animation)
this.addEventListener(MouseEvent.MOUSE_OVER, mouseOver);
this.addEventListener(MouseEvent.MOUSE_OUT,mouseOut);
function mouseOver(e:Event):void {
//show the buttons if they exist
if(btn1) btn1.visible = true;
if(btn2) btn2.visible = true;
}
function mouseOut(e:Event):void {
//hide the buttons if they exist
if(btn1) btn1.visible = false;
if(btn2) btn2.visible = false;
}
EDIT
Based off your screenshot, it looks your hierarchy is this:
Maintimeline -> pp -> btn1
Where pp is the controls for your main timeline animation.
In that case, the code should be on the Main Timeline and look like this:
pp.visible = false;
//listen for mouse over/out on `this` (the timeline whose code this on, presumably your animation)
this.addEventListener(MouseEvent.MOUSE_OVER, mouseOver);
this.addEventListener(MouseEvent.MOUSE_OUT,mouseOut);
function mouseOver(e:Event):void {
//show the buttons if they exist
pp.visible = true;
}
function mouseOut(e:Event):void {
//hide the buttons if they exist
pp.visible = false;
}
For the mouse over to work, your animation will need something in the background to mouse over, even a transparent shape or movieClip will do.
I have 5 layers with symbols on each: a, b, c, d and e.
I am trying to work out how to apply the action bellow to a, c, d and e when you hover over b.
Also is there another action similar to ' gotoAndStop(0); ' that instead of going immediately to frame 0 it goes back the way it came?
Link to .Fla http://www.fileden.com/files/2012/11/27/3370853/Untitled-2.fla
stop();
stage.addEventListener(MouseEvent.MOUSE_OVER, playMovie); function playMovie(event) { play(); }
stage.addEventListener(MouseEvent.MOUSE_OUT, stopMovie); function stopMovie(event) { gotoAndStop(0); }
stop();
Thanks
EDIT
After looking at your .fla, here is what is missing/misplaced:
Layers in flash don't mean anything other than z-order/depth. You cannot manipulate a layer in code. All your animations are on the same timeline, so they will always play together. If you want an individual item to animate without the others, you'll have to do the animation on it's own timeline (not just it's only layer). You access your symbols own timeline by double clicking it - do your animation in there.
To reference items that are on the stage, you need to give them an instance name. You do that by clicking on the item that's on the stage, then in properties panel, there is field where you can put in an instance name. For the code below to work, you'd need to give them an instance name of "a","b","c","d","e" respectively. This is different than the symbol name in your library (though it can be the same name).
One way you could do this:
var btns:Vector.<MovieClip> = new Vector.<MovieClip>(); //create an array of all your buttons
btns.push(a,b,c,d,e); //add your buttons to the array
for each(var btn:MovieClip in btns){
btn.addEventListener(MouseEvent.MOUSE_OVER, btnMouseOver); // listen for mouse over on each of the buttons
btn.addEventListener(MouseEvent.MOUSE_OUT, btnMouseOut);
}
function btnMouseOver(e:Event):void {
for each(var btn:MovieClip in btns){ //loop through all your buttons
if(btn != e.currentTarget){ //if the current one in the loop isn't the one that was clicked
btn.play();
try{
btn.removeEventListener(Event.ENTER_FRAME,moveBackwards); //this will stop the backwards animation if running. it's in a try block because it will error if not running
}catch(err:Error){};
}
}
}
function btnMouseOut(e:Event):void {
for each(var btn:MovieClip in btns){ //loop through all your buttons
if(btn != e.currentTarget){ //if the current one in the loop isn't the one that was clicked
goBackwards(btn);
}
}
}
There is no nice way to play a timeline backwards, but there are ways to do it. One such way:
//a function you can call and pass in the item/timeline you want played backwards
function goBackwards(item:MovieClip):void {
item.stop(); //make sure the item isn't playing before starting frame handler below
item.addEventListener(Event.ENTER_FRAME, moveBackwards); //add a frame handler that will run the moveBackwards function once every frame
}
//this function will move something one frame back everytime it's called
function moveBackwards(e:Event):void {
var m:MovieClip = e.currentTarget as MovieClip; //get the movie clip that fired the event
if(m.currentFrame > 1){ //check to see if it's already back to the start
m.prevFrame(); //if not move it one frame back
}else{
m.removeEventListener(Event.ENTER_FRAME,moveBackwards); //if it is (at the start), remove the enter frame listener so this function doesn't run anymore
}
}