0
votes

Here I go:

I'm doing this mobile app using AS3-AIR.

A deck of playing cards is shown in the center of the screen, and using the finger, you are able to flick off the top card to reveal the next one, using a swipe action.

It's a bird view, so only one card is showing, the user will assume that the rest of the deck is beneath the top card.

Cards are face down, so every time the user swipes a card off the screen, the same back image is displayed in the center over and over.

When the user double taps the back of a card, it flips over and the face of the card is revealed.

Here's my code (my questions come right after):

//Adding three same backs of cards to the stage:
addChild(backBlue3); //No event listener will be attached to this card
addChild(backBlue2);
addChild(backBlue1);
//backBlue1 and backBlue2 will alternate their Indexes/depths to create the illusion of an infinite deck of cards. So, when backBlue1 is thrown out of the screen, backBlue2 immediately fills the center of screen.

Multitouch.inputMode = MultitouchInputMode.GESTURE;
backBlue1.addEventListener(TransformGestureEvent.GESTURE_SWIPE, backCard_handler_swipe);
backBlue2.addEventListener(TransformGestureEvent.GESTURE_SWIPE, backCard_handler_swipe);

backBlue1.addEventListener(MouseEvent.DOUBLE_CLICK, cardFlipper);
backBlue2.addEventListener(MouseEvent.DOUBLE_CLICK, cardFlipper);

public function backCard_handler_swipe(e:TransformGestureEvent):void
    {
        //*********************************** Setting the random positions for swiped off cards *********************************** 
        //Swipe is done towards the left
        if (e.offsetX == -1) 
        {
            randomX = Math.floor(Math.random() * (randomMaxX - randomMinX + 1)) + randomMinX;
        } 
        //Swipe is done towards the right
        if (e.offsetX == 1) 
        {
            randomX = Math.floor(Math.random() * (randomMaxX2 - randomMinX2 + 1)) + randomMinX2;
        } 
        randomY = Math.floor(Math.random() * (randomMaxY - randomMinRotation + 1)) + randomMinY;
        randomRotation = Math.floor(Math.random() * (randomMaxRotation - randomMinRotation + 1)) + randomMinRotation;


        //*********************************** Creating a GreenSock TimeLineMax timeline to tween the "ejected" card *********************************** 
        var tl15 = new TimelineMax();
        tl15.addLabel("cardOnScreen"); //At this point of the Timeline (the very beginning), a "Swiping sound" will be played.

        if (e.target == backBlue1)
        {           
            //backBlue1 is tweened off the screen
            tl15.to(e.target, 1, {y:randomY, x:randomX, rotation:randomRotation, ease:Circ.easeOut});

            //backBlue2 is instantly placed in the center of the screen, behind backBlue1
            tl15.to(backBlue2, 0, {y:initialY2, x:initialX2, rotation:0, ease:Linear.easeNone}, "-=1");
            tl15.addLabel("cardOffScreen", "-=0.5"); //The exact middle of the backBlue1 tween animation
            tl15.addCallback(playSlide, "cardOnScreen"); //Playing the "Swiping Sound". 
            tl15.addCallback(swapingBacks, "cardOffScreen"); //swapingBacks function is called to place backBlue2 on top of backBlue1
        }   

        if (e.target == backBlue2)
        {
            tl15.to(e.target, 1, {y:randomY, x:randomX, rotation:randomRotation, ease:Circ.easeOut});
            tl15.to(backBlue1, 0, {y:initialY2, x:initialX2, rotation:0, ease:Linear.easeNone}, "-=1");
            tl15.addLabel("cardOffScreen", "-=0.5");
            tl15.addCallback(playSlide, "cardOnScreen");
            tl15.addCallback(swapingBacks, "cardOffScreen");
        }               
    }


public function swapingBacks()
    {
        swapChildren(backBlue1, backBlue2);
    }

public function cardFlipper(e:MouseEvent)
    {
        //Flipping of the card is working fine
    }

Here are my two main problems:

-1- The swipe gesture is not very responsive: One out of four or five swipes, nothing happens. I have to do the swipe action twice or sometimes even more to finally flick the card off.

I removed the double click listener in case of conflict with the gesture listener, the problem is still there.

Is it the standard AS3 gesture listener that is not performant?

Ideally, the swipe gesture should be as in the TINDER app: very reponsive (and with drag action).

Is there any alternative to the standard GESTURE_SWIPE solution?

Any suggestions to my code above?

-2- If the user swipes very fast, the SECOND card (beneath the first one) is swiped off, not the first. How can I avoid that?

Thanks a lot for your ideas, and thanks for reading!

BR


UPDATE ON FEBRUARY 10th


So I followed Mark Knol's advice (see answers below) and used a MOUSE_DOWN & MOUSE_UP events handlers instead of SWIPE GESTURE handlers.

The result is better, but not perfect.

The result is better, because the dragging that happens when mouse is down solves the issue of second card flying out the screen.

The result is not perfect because there's a conflict between MOUSE_DOWN/MOUSE_UP events with the DOUBLE_CLICK event: When I double tap a card, the two taps are both detected as two micro-drags of the card but also as a double tap action!!

So I did the following:

  • For a clear and big drag of the card towards the edges of the screen, the card is tweened off the screen when the mouse/finger is released.

  • For a small drag of the card, the card is tweened back to its original position. This way, in case of double tap of the card, the flipping will happen from the original position of the card.

I'm still stuck with the following issues:

  1. Why in the ANDROID AIR compiled application, the double clicking is detected, but on windows flash player the double clicking is not detected?

  2. Is there a way to solve the conflict between "double click" & "Click" events? (maybe by delaying the dragging action until the system makes sure it's not a double click occuring?)

    backBlue1.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
    backBlue2.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
    backBlue1.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
    backBlue2.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
    
    backBlue1.addEventListener(MouseEvent.DOUBLE_CLICK, cardFlipper);
    backBlue2.addEventListener(MouseEvent.DOUBLE_CLICK, cardFlipper);   
    
    function mouseDownHandler(e:MouseEvent):void
    {
        e.target.startDrag();
    }
    
    function mouseUpHandler(e:MouseEvent):void
    {
         e.target.stopDrag();
         ecartX = initialX2 - e.target.x; //ecartX is the distance of the final card position with the original one.
    
    if (Math.abs(ecartX) < limitX) //For a very small drag, the card is tweened back to its initial place
    {
        var tl25 = new TimelineMax();
        tl25.to(e.target, 0.5, {y:initialY2, x:initialX2, rotation:0, ease:Linear.easeNone}, "-=0");
    }
    
    if (Math.abs(ecartX) > limitX) //For a clear long drag, the card is tweened off the screen
    {
        //Swipe to the left
        if (ecartX > 0) 
            {
                randomX = Math.floor(Math.random() * (randomMaxX - randomMinX + 1)) + randomMinX;
            } 
    
        //Swipe to the right
        if (ecartX < 0) 
            {
                randomX = Math.floor(Math.random() * (randomMaxX2 - randomMinX2 + 1)) + randomMinX2;
            } 
    
        randomY = Math.floor(Math.random() * (randomMaxY - randomMinRotation + 1)) + randomMinY;
        randomRotation = Math.floor(Math.random() * (randomMaxRotation - randomMinRotation + 1)) + randomMinRotation;
    
        var tl15 = new TimelineMax();
        tl15.addLabel("cardOnScreen");
    
    
        if (e.target == backBlue1)
            {
                tl15.to(e.target, 1, {y:randomY, x:randomX, rotation:randomRotation, ease:Circ.easeOut});
                tl15.to(backBlue2, 0, {y:initialY2, x:initialX2, rotation:0, ease:Linear.easeNone}, "-=1");
                tl15.addLabel("cardOffScreen", "-=0.5");
                tl15.addCallback(playSlide, "cardOnScreen");        
                tl15.addCallback(swapingBacks, "cardOffScreen");
            }   
    
        if (e.target == backBlue2)
            {
                tl15.to(e.target, 1, {y:randomY, x:randomX, rotation:randomRotation, ease:Circ.easeOut});
                tl15.to(backBlue1, 0, {y:initialY2, x:initialX2, rotation:0, ease:Linear.easeNone}, "-=1");
                tl15.addLabel("cardOffScreen", "-=0.5");
                tl15.addCallback(playSlide, "cardOnScreen");
                tl15.addCallback(swapingBacks, "cardOffScreen");
            }       
    }           
    }
    
    public function cardFlipper(e:MouseEvent)
    {
     //...
    }
    
1

1 Answers

0
votes

You could consider to make your own swipe detection. Basically you register a startpoint on DOWN, and check if endpoint is greater on UP, then that is a swipe to right. Of course for a swipe to left, the other end position is lower. While dragging you move the card at touch position and on UP you tween the card to desired target position.

I also would disable user interaction (mouseEnabled, mouseChildren to false?) on the cards below the current one, enable as soon as possible after a valid swipe.