Open pleaseshutup opened 8 years ago
I'm also interested in this feature. Our use case is the same; we have an area where you can scroll, and it would be nice a delay. There is an old closed PR, #225
Are there any changes that would have to be done to the PR to be a viable solution?
Same UC here, fwiw. Hacked in a setTimeout workaround for now, but I admittedly don't feel great about that solution :)
Edit: see better option below.
I'm in the same boat. Trying to make scrolling on mobile not kick off drags. @ramiabraham how does your workaround work?
@rigoleto
drake
object in this lib as a closure, and added the scroll-intent within.Hope that helps!
@rigoleto this is a great workaround. In my case, I'm building a todo list, and pretty much the whole UI is draggable. Does this mean putting a mask over the whole viewport? And if I do that, won't I affect normal link clicks? And did you adapt the jQuery scroll intention plugin?
I would also prefer if we had control over triggering drags with different events. We use hammerjs for example and so would prefer to activate drags on press rather. The lift api could be the solution
I also needed this feature in my project to manage scroll and drag'n drop on iPad.
For anyone interested, I managed to make it work and referred to this commit: https://github.com/bevacqua/dragula/pull/225/commits/b604c08378bb8ec878ff409f0591f63b774cfd81
I edited the last dragula.js version (3.6.8) and added this code before startBecauseMouseMoved
function:
function lift (el) {
var grabbed = _grabbed = canStart(el);
if (!grabbed) {
return;
}
_offsetX = _offsetY = 0; // we could calc these on mousemove but 0,0 is simpler
startOnLift();
}
function startOnLift () {
var grabbed = _grabbed; // call to end() unsets _grabbed
eventualMovements(true);
movements();
end();
start(grabbed);
classes.add(_copy || _item, 'gu-transit');
renderMirrorImage();
}
and lift: lift,
before cancel: cancel,
.
Also, add this in your JavaScript :
document.body.addEventListener('mousedown', function (e) {
setTimeout(function(){
if(e.target.hasClass('slide')){
lifted = true;
drake.lift(e.target);
}
}, 1000);
});
and as a dragula option :
moves: function(el, source, handle, sibling) {
if (lifted) { // Manage the drag after 1 second
lifted = false;
return true;
}
}
Don't forget to initialize dragula like this:
var drake = dragula(containers, options);
I don't know why @bevacqua removed this from the API, it's working completely fine on my side 👍 .
Great stuff thanks @zetura ! Ill give it a shot once I dev this further, I've also forked dragula and started customizing the code.
Delay is crucial for my workflow as well. Can it be reinstated as an option?
I need it as well
@zetura I haven't played with that code but what do you think about submitting a pull request on that? Seems like plenty of folks want this functionality.
I quickly created a PR for this using the logic @zetura provided - I still need to test it properly
@patrickfatrick @linearza I'm actually looking to implement the functionality as a unique option (liftDelay). I just have an issue that if you wait and then scroll, you have the scroll + the mirror object. I think that @bevacqua removed the functionality because you need to put a lot of code in your implementation (that's why i'm looking to have a single option). :)
@patrickfatrick @linearza Take a look at this commit : https://github.com/zetura/dragula/commit/d2465242cef4c84c136bd72763fb7128af1ecbd6 Let me know if it works for you. I can do a pull request if it's fine for you 👍
You just have to put the option "liftDelay" for this to work:
var drake = dragula(lists, { revertOnSpill: true, direction: 'horizontal', liftDelay: 700 });
Aha - I havent worked on this in a while - but I just remembered why this implementation wasnt working for us. In our case we need to trigger a lift on press - but dragula is tightly coupled with its own gestures and the mapping of mouse to touch events. This causes a problem in our case since _grabbed
doesnt get set when our press event (handled by hammerjs) gets fired. So really - what we need is to be able to trigger and maybe explicitly set the _grabbed
element based on whatever function we choose to call. Ill see if I can figure something out as well
@zetura I tried out your change on a new ipad and it almost works but not quite. It seems to be jammed up with a scroll event where the item is trying to drag and the container is trying to scroll all at the same time making it kinda unusable.
My setup has multiple container columns that scroll horizontally off the screen (horz scroll on the columns parent container). Each column has zero or many vertically stacked items (a column can be vert scroll if it overflows). I tap and hold an item to be dragged, wait for the lift to kick in and then start dragging around. Although the drag kinda works it seems to hook with the scroll and as I drag, the container(s) are scrolling as well or at least attempting to.
I'm glad you gave this a shot though. I don't see anything inherently wrong with the code and it is how I would have gone about it as well. I can't think of a different approach right now but if I do I will chime back in on this thread. Happy to give other ideas a whirl to.
Oh, and I did discover a kinda workaround to the whole thing. On an iPad/iPhone you can simply do a 2 finger scroll to scroll the containers. Dunno about other touch devices though.
In case it helps anyone, I've been able to manage this problem by exposing the grab method from the dragula API.
I've created a pull request for this change and written a short explanation of my workaround. You can read it here: https://github.com/bevacqua/dragula/pull/375
I had the same problem and came up with the following solution, perhaps this is helpful for others.
Since in 3.0.0 drag events won't start if mousemove
or touchmove
aren't fire, you could solve this issue by using stopPropagation
. Here is an angular directive I wrote (should be easy to apply to vanilla.js - you get the idea). It uses a 1000ms press delay after which the dragging starts:
return {
restrict: "A",
link: function( scope, element ) {
var touchTimeout,
draggable = false;
element.on( "touchmove mousemove", function( e ) {
if ( !draggable ) e.stopPropagation();
});
element.on( "touchstart mousedown", function( event ) {
touchTimeout = $timeout( function() {
draggable = true;
}, 1000 );
});
element.on( "touchend mouseup", function( event ) {
$timeout.cancel( touchTimeout );
draggable = false;
});
}
};
@haschu's solution worked pretty well for me, with some modifications. Mainly, dragging before the delay timer makes it 'draggable' will then cancel the timer, which solves an issue where a mouse dragging the item would appear to have no effect at first, and then further mousemoves would have the item 'catch up'.
And this one's for Angular 2 and TypeScript, also obviously it's enabled only for touch devices.
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({ selector: '[delayDragLift]' })
export class DelayDragLiftDirective {
dragDelay: number = 200; // milliseconds
draggable: boolean = false;
touchTimeout: NodeJS.Timer;
@HostListener('touchmove', ['$event'])
// @HostListener('mousemove', ['$event'])
onMove(e: Event) {
if (!this.draggable) {
e.stopPropagation();
clearTimeout(this.touchTimeout);
}
}
@HostListener('touchstart', ['$event'])
// @HostListener('mousedown', ['$event'])
onDown(e: Event) {
this.touchTimeout = setTimeout(() => {
this.draggable = true;
}, this.dragDelay);
}
@HostListener('touchend', ['$event'])
// @HostListener('mouseup', ['$event'])
onUp(e: Event) {
clearTimeout(this.touchTimeout);
this.draggable = false;
}
constructor(private el: ElementRef) {
}
}
<!-- in your template -->
<div class="some-draggable-div" delayDragLift></div>
Have implemented this in an ES5 react project and I think it's broken in >= iOS 11.3, I cannot stop the propagation of the touchmove
event, possibly related to #426
Class methods:
handleTouchStart: function () {
var _this = this;
this.touchTimeout = setTimeout(function () {
_this.isDraggable = true;
}, this.dragDelay);
},
handleTouchMove: function (e) {
if (!this.isDraggable) {
// this doesn't seem to do anything
e.stopPropagation();
}
},
handleTouchEnd: function () {
clearTimeout(this.touchTimeout);
this.isDraggable = false;
},
Within the render method:
<div
className='draggable-item'
onTouchMove={this.handleTouchMove}
onTouchStart={this.handleTouchStart}
onTouchEnd={this.handleTouchEnd}
>
{this.props.children}
</div>
The call to stop the propagation of the event in handleTouchMove
simply doesn't do anything and so dragging is never blocked. Quite frustrating! Right now I'm wondering if reverting to dragula@2.0.0
will solve this by getting the delay
option back.
Reverted to v2.1.1
, added delay via the option and it now works. I’ve seen some PRs open to add this option back, imo this is a much cleaner API than having to manually stop the propagation of another event. Not sure why it was removed.
aaaand have discovered that this breaks a all of my ordering flows. Back to the drawing board. Am now back on v3.7.2
and will continue to investigate how to get this working.
@rohan-deshpande @cormacrelf @haschu Tried your solution but could not make it work.
I attached to mousemove and touchmove events on draggable item and call e.stopPropagation();
but
dragApi.on('drag', (el) => { console.log('drag');
Still shows that drag is performed and shadow is shown. Am I missing something? Thanks
@radenkozec Sorry, my workaround is almost 4 years old and I've never used dragula since... 😕
any updates on this issue ?
any way to use delay in newest dragula?
In order to make this library a bit more touch friendly it will help to be able to delay dragging until after a second of holding the finger down without moving. This way a user can drag if they want or also scroll an area on a touch device.
I see there was a commit with a new method called "lift" which will work perfectly for me. When will this be available in dist folder?