Jump to content
Search Community

Draggable#update() doesn't work inside onDragStart callback since version 1.16.0

dened test
Moderator Tag

Go to solution Solved by GreenSock,

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

Hello,

 

I'm trying to align the center of a draggable element to the cursor position at start of dragging.

var element = document.getElementById('block');
var draggable = new Draggable(element, {
  onDragStart: function centerAlignElementToCursor(e) {
    var rect = element.getBoundingClientRect();
    var centerX = (rect.left + rect.right) / 2;
    var centerY = (rect.top + rect.bottom) / 2;
    var deltaX = e.clientX - centerX;
    var deltaY = e.clientY - centerY;

    TweenLite.set(element, {
      x: '+=' + deltaX,
      y: '+=' + deltaY
    });
    draggable.update();
  }
});

See full code in the referenced Codepen.

 

It works as expected in 1.15.1, but doesn't work since 1.16.0.

It looks like the call to draggable.update() is ignored inside the onDragStart callback.

 

How it can be fixed?

See the Pen WQNBxr by dened (@dened) on CodePen

Link to comment
Share on other sites

Hi and welcome to the GreenSock forums,

 

Thanks so much for the simple demo.

Sorry about the issue. At the moment can't recall what may have changed from 1.16.0 but it seems that if you pass in true for the applyBounds parameter it will work with the latest version

draggable.update(true);

http://codepen.io/GreenSock/pen/NGWZqg

 

Does that work for you?

  • Like 2
Link to comment
Share on other sites

Thanks for quick reply, Carl!

 

Passing true for the applyBounds apparently works.

 

And I've found that this modification also helps:

TweenLite.set(element, {
  x: '+=' + deltaX,
  y: '+=' + deltaY,
  immediateRender: false,
  onUpdate: function() { draggable.update(); }
});
  • Like 1
Link to comment
Share on other sites

Although rare, there may be times when you need to update during actual dragging, and I kept finding out the hard way that this is a bad thing to do. What happens is that it will throw the draggable into a max call stack, which will lock up your browser for a good 30 seconds or more. Not fun. I figured out that if you defer the update it won't lock up the browser. You can do this by calling the update method in a setTimeout function.

setTimeout(function() {
  draggable.update();
});

So when you would ever need to do this? Try keeping your draggable bounded to a circle. I ended up changing how the updates are handled, but here is part of the project I was working on.

 

See the Pen Qbgyoq by osublake (@osublake) on CodePen

  • Like 2
Link to comment
Share on other sites

Cool! No more lockups. I didn't even bother to ask if this was an issue, and just worked around it.

 

I knew about delayedCall(), but I'm not sure if there is an easy way to cancel it. When I defer something, especially if it is involves a lot of calculations, I normally set it up so that it can be canceled like this.

var queue;

function queueCallback(callback) {        
  if (queue) clearTimeout(queue);    
  queue = setTimeout(callback);
}
Link to comment
Share on other sites

Absolutely, Blake:

 

Option 1: tween reference

Under the hood, a delayedCall is just an empty tween with an onComplete and onReverseComplete pointing at your function. So you can do this:

var queue = TweenLite.delayedCall(1, yourFunction);
//then later...
queue.kill();

Or you could even pause()/resume() it, or mess with the timeScale() :)

 

Option 2: TweenLite.killDelayedCallsTo()

You don't even have to maintain a reference. For example:

TweenLite.delayedCall(1, yourFunction);
//then later...
TweenLite.killDelayedCallsTo(yourFunction);

Tip: killDelayedCallsTo() is actually the EXACT same thing as killTweensOf(). Since under the hood a delayedCall() is just a tween (with the function serving as the tween's target), you can TweenLite.killTweensOf(yourFunction); 

 

The benefit of doing this instead of your own setTimeout() is that it remains synchronized with the animation engine and it's probably a bit more performant since a single requestAnimationFrame ticker is driving everything whereas a setTimeout() basically has a clock of its own. 

  • Like 2
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...