Jump to content
Search Community

Draggable on mobile : how to manage horizontal draggable and native vertical scroll (webkit)

Webls test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Hi all,
this is my first post on this forum. 
 
I am working on a mobile calendar which requires to manage a large horizontal drag area combined to a important vertical scroll for a mobile web application (in webkit browser : Safari / Chrome)
 
You will find a mockup attached to this post in order to understand what i am trying to do.
 
 post-17768-0-19141900-1399297331_thumb.png

This calendar has :
- on left side : a vertical list of people (called "fixedView")
- on right side : a vertical list of blocks which has a horizontal scrollview (using Draggable) in order to swich days by using drag gesture on axis : x (called "InnerView")
 
These two sides are embedded in a "mainView" which is the page container for calendar. (Please have a look to attached file).
 
In fact, i am trying to use a horizontal Draggable (InnerView) into a container which has a vertical scroll (mainView)
 
I made a lot of research , check mobile performances (Galaxy S3 mini to iPhone5) and tests to try :

"Scroll In Scroll" : Adding a vertical Draggable for mainView + Adding a horizontal Draggable for InnerView

  • it "works badly" but don't know how to control vertical & horizontal in order to "lockAxis"; right now you can do diagonal drag which is not good (and bad perfs of course)
     
  • moreover : with this method, i removed native scroll (provide by webkit with -webkit-overflow-scrolling: touch; which brings low perfs on vertical scroll
     
  • => My Conclusions :
    • using Greensock Draggable to manage vertical scroll is not possible cause we have very bad perfs on low devices
       
    • I have to use native scroll provided by webkit for vertical scroll (best perfs.) and use Draggable for horizontal scroll (in InnerView) only

Do you agree ? 
 
So, this is the problem i can't resolve : 
 
When i create a new Draggable for InnerView (params below) with {type : "x"}, i can't do a vertical scroll anymore (for sure).
What i would like to do is to detect when user is doing a horizontal or vertical scroll on InnerView (by a swipe or drag gesture)  .

  • If User is scrolling horizontaly => we use Draggable Instance for InnerView only
  • if User starts scrolling verticaly => we use a "prevent default" for Dragable to release touch bind and so let the browser do a native vertical scroll (webkit)

So for this 2nd point, i don't know how to do this. (I had a look on google, GSAP forums, Docs...)
I tried to bind the event with onDragStart method but didn't get it.

Do you this this is the right approach ?
 
I cant use a Draggable instance with {type : "x,y"} because it would mean that only "InnerView" will scroll vertically but in our case we need to scroll the entire mainview (which include FixedView + InnerView => so the mainView
 

I had a look to "scrollLeft" and "scrollTop" properties and several code pens but i am still blocked.
 
Hope my post is understandable; if you need more details, do not hesitate.

 

 

Here Draggable Instance for InnerView :

innerView = Draggable.create(innerViewEl, {
	type			: "x",
	bounds			: targetStageEl,
	throwProps		: true,
	force3D			: params.stage.force3D,
	edgeResistance	: params.stage.edgeResistance,
	dragResistance	: params.stage.dragResistance,
	// dragClickables	: true,
	lockAxis 		: true,
	// zIndexBoost		: false,
	snap: {
	x: function(endValue) {
		return Math.round(endValue / contextInterface._width) * contextInterface._width;
	}
}
});

Here DraggableInstance for MainView (bad perf for vertical scroll)

mainView = Draggable.create(mainViewEl, {
	type		: "y",
	bounds		: {minY:0, maxY:-3000}, //SimulateLongScroll
	throwProps	: true,
	force3D		: false, // params.stage.force3D,
	edgeResistance	: params.stage.edgeResistance,
	dragResistance	: params.stage.dragResistance,
	dragClickables	: true,
	lockAxis 	: true
}); 

 
Thanks in advance.

Link to comment
Share on other sites

Hi and welcome to the GreenSock forums.

 

In terms of performance in low-end devices, that has nothing to do with GSAP, is just the device's hardware coming short.

 

Now in terms of allowing the native vertical scroll when the touch event started in the Draggable instance. honestly I don't know. One chance is to detect the direction of the swipe event and based on that disable the Draggable instance in order to allow vertical scroll. For that you could check the Draggable instance's pointerY value by adding an event listener to GSAP's tick event and if the variation of a stored value is bigger than a specific threshold you can disable the Draggable instance and on touch end enable it again.

 

Another option could be use a touch library, like Hammer (http://eightmedia.github.io/hammer.js/) in order to detect the swipe direction and disable/enable the Draggable instance, also based on a specific threshold.

 

Yet the simplest solution is use two separate Draggable instances, one for the main container (vertical scroll) and another for the horizontal one. Of course I'm not aware of the intricacies of your app, but at least this codepen sample works great on a very small device (single core 800Mhz CPU and Adreno 200 GPU):

 

http://cdpn.io/LHpCF

 

Finally a live sample would give us more light in terms of what you're trying to achieve. Is always better to see real code working even with all the detailed information you've provided. Please try to create a simple example in Codepen.

 

Rodrigo.

  • Like 3
Link to comment
Share on other sites

  • 4 weeks later...

Hi,

 

Further to this discussion, I'm having some issues with the performance of disabling draggable instances, the site I am making will be for smart phones and I am seeing slowdown and performance problems when testing on iPhone 4S iOS 7.1.1 (it's worse with other less powerful phones).

 

My setup is similar to Webls, I have a main container that has draggable scrollTop on it and inside that I have many elements with draggable x set on them.

 

Here is a codepen to demonstrate :

 

See the Pen gbBcu by x0b (@x0b) on CodePen

 

You'll notice that when dragging vertically you can also drag the horizontal draggables and vice-versa. Although it works pretty smoothly, this is unwanted behaviour as I want to either drag vertically or drag one of the horizontal draggables on it's own.

 

The following app exhibits the exact behaviour I want to recreate, I think it works very smoothly and is a great UX, to be able to recreate it with Greensockk would be amazing!

 

https://itunes.apple.com/gb/app/bbc-news/id377382255?mt=8

 

So, this code pen demonstrates the desired relationship between vertical and horizontal:

 

See the Pen pcgEm by x0b (@x0b) on CodePen

 

The problem now is performance, when i flick one of the horizontal rows I get a stutter where it is disabling the vertical drag.

 

It's also very hard to do a horizontal drag every time because the threshold between a horizontal gesture and vertical gesture is so close. This is also apparent when trying to drag vertically when touching on one of the rows.

 

I wonder if you have any ideas about how I can overcome this performance problem? It seems disabling and/or enabling a draggable instance uses up enough juice to causes jerky animations. I'm not sure what goes on under the hood but maybe there is a way to do a lighter version of disable/enable that won't cause glitches?

 

Even if there was a way of adjusting the '3px' click threshold to be a higher value, that might help?

 

I've also tried setting the dragResistance to 1 in the hope it would disable in a 'light' way but that didn't work out well, although that approach seems like it could be good... :?

Link to comment
Share on other sites

Hi,

 

For the performance issue I don't know what to tell you, I believe that has more to do with hardware limitations than other possible cause, maybe if you reduce the amount of element's that could help. Remember that by default Draggable uses force3D:true, in order to improve peformance. That creates GPU layers in order to relief the browser, but some devices' GPUs are very limited, specially low end devices, so if you pass too many instances to the GPU it hurts performance instead of helping.

 

As for lock the Draggable direction, you could check how much the pointer position has changed before disabling the other instance(s). For example if you want the threshold to be 15px you can check the instance's x value in the onDrag event and if the value exceeds that amount you disable the other instances. Finally in the onDragEnd event you can enable the other instances. Sorry but I'm a little short of time now so I can't create a sample, maybe later I'll get to it.

 

Rodrigo.

Link to comment
Share on other sites

Hi,

 

I've managed to get it working exactly as required.

 

See the Pen qmGis by x0b (@x0b) on CodePen

 

When not dragging I set the draggables to dragResistance 1 so they can't be dragged. Then as soon as a touch is registered I start to record the direction the drag is going whilst touchmove fires and then change the dragResistance of the desired draggable to suit, then reset all dragResistance settings back to 1 when the touchend event fires.

 

I could probably optimise this more by just using draggables built in drag events but I'm very happy with how it works and I don't get any stuttering like I did when using enable() and disable().

 

Cheers! 

  • Like 2
Link to comment
Share on other sites

  • 4 months later...

Hi x0b,

 

First of all, great stuff! Have been trying to figure out a solution for the same problem...

 

But, your example wasn't working on my Android device (sony xperia z2,android 4.2.2)... 

Changed line 16 from

touch = e.type === 'touchstart'? e.originalEvent.touches[0] : e.originalEvent;

to

touch = e.type === 'touchmove'? e.originalEvent.touches[0] : e.originalEvent;

and it worked for me.

 

Thanks for your work!

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...