Jump to content
GreenSock

cmm

Draggable snap to type 'x,y'

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

Trying to create a draggable snap to pages on a site:  sample codepen found

See the Pen gltzw by cmmize1 (@cmmize1) on CodePen

See the Pen gltzw by cmmize1 (@cmmize1) on CodePen

 - no ScrollToPlugin

 

and here: http://www.pradev.com/one-page/sample.html - with ScrollToPlugin

 

On the Draggable.create - using a type: 'scroll' or a type:'x,y' - I am having problems getting the proper snap behavior on the x axis (dragging left - see page two of the sample).  Any help figuring out the dynamics of the snap values to behave proper for this type of site would be greatly appreciated.  The Y scrolling snap work great.

 

Another thing I noticed changing the Draggable.create to a type: 'x,y' - The return values for jquerys .position varies on the pages. Shows up in the function scrollTo console.log.   I'm pretty sure it has to do with the wrapper div that gets created with Draggable.create - type:'x,y'?

 

I have commented out the code for the Draggable.create - type:'x,y' so as to test both types.

 

Thanks for any help, Chuck

 

 

Link to comment
Share on other sites

Hi Chuck,

 

Thanks for providing the demos, and great job by the way, very interesting stuff you're doing.

 

In order to snap and since your elements have a static width, you can pass an array to the snap function with the values, you can see this great sample Jamie did some time ago while helping me with something similar:

 

See the Pen FnKba by jamiejefferson (@jamiejefferson) on CodePen

 

And applied to what I was aiming:

 

See the Pen pzIgJ by rhernando (@rhernando) on CodePen

 

Just pass an array with the possible values on each axis:

snap:
{
  x:[/*values*/],
  y:[/*values*/]
}

If you want to keep the function and math logic you'll have to create an individual snap function for each axis, because now all your snap points are based on the element's height, which is smaller than the width, therefore causing the odd snapping when you drag/throw on the x axis:

var maxScroll = content.scrollHeight - content.offsetHeight;

Draggable.create(content, {
    type:"scroll",
    edgeResistance:0.5,
    throwProps:true,
    onDragStart: killTweens,
    snap: function(endValue) {
        var step = maxScroll / (floors - 1);
        return Math.round( endValue / step) * -step;
    }
});

// create a snap function considering the floor and the room
// use height for the floor snap(Y axis)
// and the width for the room snap(X axis)

var maxScrollY = content.scrollHeight - content.offsetHeight;
var maxScrollX = content.scrollWidth - content.offsetWidth;

As you can see you already have those variables.

 

Also for what I see you're creating the Draggable instance on the container of the floors/rooms, therefore when you use a scroll-type Draggable what you drag is the content inside that element. When you create a "x,y"-type Draggable you drag the container with all it's children inside, therefore the result is not the same. To achieve something similar you should create an element inside the container that wraps the content, and create the "x,y" Draggable on that element.

 

As for the second question you get the correct position if you set a position property to the content wrapper element:

#contentwrapper
{
  margin: 0 auto;
  position:relative;
}

I don't know why is this, I've looked about it in google but haven't found an answer. Anyway when I need something like this I always do a little calculation between the parent and child offset. Perhaps another user could enlighten us about this.

 

Rodrigo. 

Link to comment
Share on other sites

Thanks for providing the CodePen demos.

 

I think lockAxis will help you here.

 

lockAxis : Boolean - if true, dragging more than 2 pixels in either direction (horizontally or vertically) will lock movement into that axis so that the element can only be dragged that direction (horizontally or vertically, whichever had the most initial movement) during that drag. No diagonal movement will be allowed. Obviously this is only applicable for type:"x,y" and type:"top,left" and type:"scroll" Draggables. If you only want to allow vertical movement, you should use type:"y" or type:"top" or type:"scrollTop" or if you only want to allow horizontal movement, you should use type:"x" or type:"left" or type:"scrollLeft".

 

Also keep in mind that you can define unique snap functions for both the x and y axis.

 

Visit: http://greensock.com/draggable/

 

Beneath the first demo, select "Snap end position to grid" and "Lock axis"

Take a look at the code, I think that will help you quite a bit.

 

It think it might get tricky though if you don't want folks throwing and dragging to the left and right if there isn't a page over th

Link to comment
Share on other sites

Thanks Rodrigo and Carl for the quick response.  I did get the behavior correctly for both the x and y dragging (

See the Pen FnKba by jamiejefferson (@jamiejefferson) on CodePen

).  Now on to stopping the throwing and dragging to right (only testing that direction for now) when there is not a page.   Is there a way to grab the current div that a user might be throwing or dragging on?  If I can do that then I can reset the bounties when attempting to go right. I have tried looking at 'this' in a onDrag, but it's the draggable object.  

 

Thanks again for your help,  Chuck

Link to comment
Share on other sites

Mhh, I believe not directly through Draggable, but there are a couple of things you can try.

 

Even with Draggable having it's own set of event handlers you can still call jQuery's mousedown event on the Draggable target or any of it's children elements. Like that you can see if the current element has other to the right, thus enabling the Drag on the X axis and disabling if there are none.

 

Another possibility is using the onThrowComplete event, you can kill the Draggable instance and create a new one and checking the current visible object you can create a Draggable instance for both axis or just one, depending if there are elements to the right. For this you'll need to know the index of the elements that have others to the right and through a simple counter you can check which element is visible to the user right now.

 

It's a little bit of work but nothing too complicated.

 

In Draggable's callback events this always refers to the Draggable instance. If you want to point to the element being dragged you should call this.target, but be aware that in this case it refers to the main container and not any of it's child elements.

 

Rodrigo.

Link to comment
Share on other sites

If you're trying to figure out what element is being dragged, you could just set up on onDragStart callback (or event listener) and onDragEnd callback (or event listener) to store it in a variable or something. Just keep in mind that Draggable allows MULTIPLE things to be dragged at the same time (like on a touch device, you could use two fingers on two different elements). 

var dragging;
...
onDragStart:function() {
    dragging = this.target;
}
onDragEnd:function() {
    dragging = null;
}

Then you'd just have to look at the "dragging" variable to see what element is dragging. You might want to make it into an array to accommodate multiple elements being dragged on touch devices though. Hopefully you get the concept though. 

 

Another option is to check the isDragging property of each of your Draggable instances - that'll tell you if it's currently being dragged. 

  • Like 1
Link to comment
Share on other sites

Hi Jack,

 

Thanks for the reply.  I'm trying to grab the div that is active in the viewport...  it would be a child of the content(draggable instance content).  My hopes is to restrict the the drag boundaries to keep the viewport on defined divs.  I have attached an image to help explain this. post-3571-0-60795000-1394208750_thumb.png

 

 Here is an example of some close to this design using Kirkas ascensor lib. (http://www.pradev.com/Ascensor-Sample/examples/example_swipe.html) As you can tell from the example it lacks the elegance of your software and requires manually defining "floor/rooms".   

 

Thanks again for all your support, I've really enjoyed this product.

Link to comment
Share on other sites

Hi Chuck,

 

I believe that an alternative is to kill the current Draggable instance onThrowComplete. Then create the Draggable again and check to see if there are any objects in the X axis and set the Draggable type to "scroll", otherwise set the type to "scrollTop" in order to drag along the Y axis only.

 

Although Carl or Jack could point if there's a way to limit the scroll axis once the Draggable instance has been created, perhaps via scrollProxy, or perhaps update the Draggable instance type on the fly, but as far as I know is not possible, that's why I suggest to kill and create the Draggable instance again. That could be done through a function in order to reduce the amount of code.

 

Rodrigo.

Link to comment
Share on other sites

Rodrigo's suggestion is probably the cleanest, although you should be able to alter the bounds if you want by editing the vars config object, like yourDraggableInstance.vars.bounds = {...new bounds...} and then you can call update(true) to force a refresh. 

  • Like 1
Link to comment
Share on other sites

Thanks for all the help on this.  I did implement Rodrigo's suggestion.  

Be sure to resize the browser to the div size(will fix that later - limitation of the viewport plugin used).

On codepen: 

See the Pen gltzw by cmmize1 (@cmmize1) on CodePen

 

Sample site:  http://www.pradev.com/one-page/sample.html

 

On an ipad (both chrome and safari) the screen flickers at the end of the drag, I'm thinking it is when I destroy the draggable instances and recreate the new one.  Any ideas on how to stop that?

 

In your opinion, is this the best way to go about this for a one page multi-directional web site?

Link to comment
Share on other sites

Hi Chuck,

 

Looks very good, nice job!!

 

As for the flickering I don't know, maybe is an ipad issue, it would help to know which version of iOS you're using. Jack reported some time ago that there's a performance issue on iOS7:

 

http://www.greensock.com/ios7/

 

If your ipad is on iOS 6, maybe try Jack's suggestion of updating the bounds of the Draggable instance. Basically works in the same way you're doing it right now, but instead of kill and create the Draggable instance, you update the bounds onThrowComplete.

 

Another possibility is add a small delay in the function that destroys the Draggable instance, perhaps 10 to 20 milliseconds, see what happens.

 

Rodrigo.

Link to comment
Share on other sites

One thing that I've seen cause a "flash" in some browsers like Chrome is when an element goes from a 3D to 2D context (or visa-versa). When any kind of 3D transform is applied, it generally gets its own GPU/compositor layer, so I suspect the flash has to do with it shifting to/from that mode. To avoid the Flash, don't switch contexts (keep it 3D if possible). 

  • 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.
×