fljot / Gestouch

Gestouch: multitouch gesture recognition library for Flash (ActionScript) development.
MIT License
355 stars 85 forks source link

Swipe and Panning Image #18

Closed VictorYew closed 11 years ago

VictorYew commented 12 years ago

Hi fjlot,

I have an issue whereby the swiping and panning listener are conflicting each other. I am trying to apply pan and zoom gestures to the image gallery and they work well initially. BUT, when I add the swipe gesture to the stage to change the view, they ain't working well. Sometimes, the image can be zoomed and panned but mostly both the gestures become unresponsive or delayed. Besides, it triggers the swipe event when i try to pan the image. Is there a work around for this?

Best regards, Victor

fljot commented 12 years ago

I don't remember why, but it appears to be that I changed Swipe slop property to 0. * ...After digging in code... * Oh and I also removed slop from recognition calculations... So I'm bringing it back. Try this https://github.com/fljot/Gestouch/tree/hotfixes/swipe-slop and give me feedback so I could merge this hotfix into master branch and release new swc.

Also I recommend you to define (if didn't) swipe direction so that gesture will be more precise. Also I strongly recommend to use Transform gesture instead of Pan/Zoom combination mainly because of one-two fingers and swipe and gestures relations problems.

Now a bit of more low-level explanation. What is slop? For swipe, pan, transform it's a distance from the beginning of the touch where nothing happens yet. No recognition and not even failing(for Swipe). I kept slop in failing logic for swipe, but removed it for recognition logic for some reason (looks like was not the smartest decision). So how does it work now? Pan (or Transform) on the image / gallery container has the same slop as Swipe on stage. You move your finger a bit (less then slop) — nothing happens (but before swipe could happen since there was no slop in recognition logic). You move your finger more — Pan/Transform recognizes first (since they are deeper in display list), but Swipe might also be ready to be recognized. But by default Pan will abort it since Pan recognizes first.

Now you ask "But this way my Swipe will be never recognized?!". Well yes, unless you do some tricks.

  1. Set delegate for your stage Swipe, where gesturesShouldRecognizeSimultaneously method should return true in case one gesture is your stage Swipe and other one is image Transform. This way Swipe won't be aborted by Transform and you can react on them somehow. NB! Don't forget to do something about Transform once Swipe is recognized (since Transform is in tracking progress.. need to be stopped I believe).
  2. Use requireGestureToFail API (transform.requireGestureToFail(swipe)). No extra coding will be needed, but some latency will appear in your transform since it will "wait" (using time or swipe-failing-movements) till Swipe fails.

Now to the question "Why single Transform and not Pan + Zoom?". Well, once Swipe is aborted(failed) somehow with the first finger, you will still have to repeat same for the second finger once it comes... Might be a bit messy/tricky.

Ask if something is not clear! And tell me if it works well with the recent changes. Good luck.

VictorYew commented 12 years ago

Now, I have included the hotfixes. Initially, the image transform is working fine with first trigger (without any latency happened in my first trial). BUT, I have problem implementing the two methods(1 & 2) you suggested and thus the Swipe gesture only trigger when I swipe across the stage, not the image itself. Now, here is the problem, even if the Swipe gesture is triggered, it will push to other view in my case. Then when I push back to this view, both the transform and the swipe gesture are not working anymore. The following is my code:

private function galleryHandler():void
        {

            transformGesture = new TransformGesture(gallery);
            swipeGesture = new SwipeGesture(this);
            transformGesture.addEventListener(org.gestouch.events.GestureEvent.GESTURE_BEGAN, onGesture);
            transformGesture.addEventListener(org.gestouch.events.GestureEvent.GESTURE_CHANGED, onGesture);
            swipeGesture.addEventListener(org.gestouch.events.GestureEvent.GESTURE_RECOGNIZED, onSwipe);
        }

        private function onGesture(event:org.gestouch.events.GestureEvent):void
        {
            const gesture:TransformGesture = event.target as TransformGesture;
            var matrix:Matrix = gallery.transform.matrix;

            // Panning
            matrix.translate(gesture.offsetX, gesture.offsetY);
            gallery.transform.matrix = matrix;

            if (gesture.scale != 1)
            {
                // Scale
                var transformPoint:Point = matrix.transformPoint(gallery.globalToLocal(gesture.location));
                matrix.translate(-transformPoint.x, -transformPoint.y);
                matrix.scale(gesture.scale, gesture.scale);
                matrix.translate(transformPoint.x, transformPoint.y);

                gallery.transform.matrix = matrix;
            }
        }

        private function onSwipe(event:org.gestouch.events.GestureEvent):void
        {
            const swipe:SwipeGesture = event.target as SwipeGesture;

            if(swipe.offsetX < 0)
            {
                this.navigator.pushView(Gallery2);
            }
        }

I hope you can provide me with some clue. I apologize for not having enough understanding with your methods.

fljot commented 12 years ago

Upload the whole project somewhere (or better try to exclude all irrelevant parts).

VictorYew commented 12 years ago

Please take a look. www.sundancepost.com/test/GestouchTest.fxp

fljot commented 12 years ago

Zip please. Not everyone have Flash Builder.

VictorYew commented 12 years ago

Sorry. Here you go. www.sundancepost.com/test/GestouchTest.zip

fljot commented 12 years ago

>>> Then when I push back to this view, both the transform and the swipe gesture are not working anymore Well of couse they won't work because you dispose them for good in onViewDeactivate!

>>> and thus the Swipe gesture only trigger when I swipe across the stage, not the image itself Well yea, as I explained, Transform recognizes first, so it aborts Swipe recognition. But if you move below the image in your example Transform gesture does not see any touches, but Swipe does. So it can be recognized without any problems.

Otherwise

  1. Swipe supposed to be used for navigation here, right? So it has to be a single gesture. Just create one in the main class. And, as I said earlier, set swipe.direction = SwipeGestureDirection.HORIZONTAL.
  2. In your gallery views set transform.requireGestureToFail(swipe); (you should get reference to that swipe gesture instance somehow. Quick & dirty way to make it static).
VictorYew commented 11 years ago

I made changes according to your methods, now both the transform and swipe gesture are working. BUT, how am I suppose to set the minimum distance offset for swipe? I have tried swipe.minOffset = 100 but it has little effect. I am doing this because now a slight change of OffsetX will trigger the swipe gesture.

Please take a look at my code: www.sundancepost.com/test/GestouchTest.zip

fljot commented 11 years ago

Swipe is recognized (generally speaking) when (offset> slop && (velocity>= minVelocity || offset >= minOffset)) and failed after maxDuration milliseconds have passed without recognition. By default these values are well matched and gives you pretty much same behaviour as native ones on iDevices. But if you really need you can play with them (e.g. increase minVelocity to prevent from early recognition, set desired minOffset and adjust maxDuration. NB! Increasing maxDuration will lead to even greater latency for TransformGesture recognition if you have that requireGestureToFail binding since swipe will fail later).

VictorYew commented 11 years ago

Now everything is working now. But, it seems like inevitably the transform gesture will be recognized first when launch. So, when I try to swipe on top of the image, it will pan instead. Then, when I swipe the image again this time, swipe will be recognized. You mentioned to move the swipe gesture behind the image and I believe I did so, and I have played around with the minVelocity and minOffset as well, but to no avail.

Is there a way to allow only the zoom function activated in the transform gesture when launch? I mean it only allows the user to pan the image when the image is scaled. In this way may be, the swipe gesture can be recognized first.

fljot commented 11 years ago

If you bind them using requireGestureToFail transform shouldn't be recognized untill swipe fails (due to time or wrong direction). Looks like you did something wrong if you experience this kind of problems.

>>>I mean it only allows the user to pan the image when the image is scaled. In this way may be, the swipe gesture can be recognized first. I don't think it will help much since this way swipe will be impossible once image is scaled. So let's get back to the proper implementation.

Update the archive so I could take a look again.

VictorYew commented 11 years ago

I've added the Double-Tap gesture as well but i don't think it will ruin other gestures. So, here you go. Please take a look. Thx. www.sundancepost.com/test/GestouchTest.zip

fljot commented 11 years ago

Oh man... You've put transformGesture.requireGestureToFail(swipeGesture); in a wrong place (in gesture handler). It's a configuration statement, it should be in place where you define your gestures (galleryHandler()). And what for did you write swipeGesture.requireGestureToFail(transformGesture);? TransformGesture won't fail... What's the point of that anyway?

So:

  1. swipeGesture = new SwipeGesture(FlexGlobals.topLevelApplication);
  2. comment out your minOffset, minVelocity, maxDuration for a moment
  3. in galleryHandler() add transformGesture.requireGestureToFail(swipeGesture); and remove all other requireGestureToFail calls This way if works as I explained above.

Check the examples project and Apple videos again to understand how requireGestureToFail works.

VictorYew commented 11 years ago

Ah, my bad for a lack of understanding. Now, everything is working fine as what I expected. Just one thing when panning the image, the finger has to be moved diagonally from right to left so that it does not accidentally trigger the swipe gesture.

fljot commented 11 years ago

Well yea, the thing is swipe and pan are not really well stackable. So it's not the very best interaction design to have swipe for navigation (it's a "hidden" gesture btw!) and some panning for panning. All you can do about your current situation is just to play with those swipe configuration properties.

VictorYew commented 11 years ago

Alright then. Thank you for your help anyway. It's a nice piece of work you did there.

surferdave commented 11 years ago

I have been looking for these examples for a few days. I downloaded the samples but I dont use flash fdt. Installed it but the when I imported the project it told me I need the pro. It would be great if you could add this code to the examples package to make it easier to find and learn. Great work though and thank you looking forward to building my app with it.

fljot commented 11 years ago

@surferdave that project should be also compilable out of command line, no FDT or other IDE required.

sidesixmedia commented 11 years ago

hi fljot. We have a scenario where we'd like to have an item respond to two different actions: a "touch and drag" for real-time placement and 2.) a SWIPE to signify a specific motion (most likely a pre-defined slide in the direction of the swipe). We initially thought using PAN and SWIPE gestures would work, but clearly they aren't easy to distinguish or use together.

Is there a method you'd recommend to accomplish this using GesTouch? Or is our best option to rely on SWIPE and then somehow create our own "touch and drag" functionality?

Thanks!

fljot commented 11 years ago

@sidesixmedia

pan.requireGestureToFail(swipe);
swipe.direction = some direction so that swipe fails ASAP

https://github.com/fljot/GestouchExamples/blob/master/src/org/gestouch/examples/views/DependentSwipingGesturesView.mxml

sidesixmedia commented 11 years ago

Thanks, fljot. We'll try following your example. We were using:

swipe.canBePreventedByGesture(pan);

but got mixed results, so we'll have to keep plugging away.

Is there a way to import the GesTouch examples in Flash Builder even though they don't have a project structure? We haven't been successful with that so far.

Thanks again!

saar62097 commented 10 years ago

fljot,

Just wanted to say: for all you input and patience: wow, respect!

fljot commented 10 years ago

@saar62097 thnx)