-
Notifications
You must be signed in to change notification settings - Fork 786
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Snap to dropzone? #79
Comments
Hi. This is a good place to ask :)
To achieve this, you can set snapping to be the centre of the current dropzone every time a interact('.block').snap({
mode: 'anchor',
anchors: [],
range: Infinity,
elementOrigin: { x: 0.5, y: 0.5 },
endOnly: true
});
interact('.dropzone')
.on('dragenter', function (event) {
var dropRect = interact.getElementRect(event.target),
dropCenter = {
x: dropRect.left + dropRect.width / 2,
y: dropRect.top + dropRect.height / 2
};
event.draggable.snap({
anchors: [ dropCenter ]
});
})
.on('dragleave', function (event) {
event.draggable.snap(false);
});
I've added an
In your case, interact('.dropzone').dropzone({ overlap: 'center' }); You can get the latest version here https://rawgit.com/taye/interact.js/master/interact.js |
Amazing taye! Just one more things since I have you "on the line" :) How should I make the block go back (animate) to its original position if it's not dropped on a dropzone? |
If you record the center point of the draggable at var startPos = {x: 0, y: 0};
interact('.block')
.on('dragstart', function (event) {
var rect = interact.getElementRect(event.target);
// record center point when starting a drag
startPos.x = rect.left + rect.width / 2;
startPos.y = rect.top + rect.height / 2;
// snap to the start position
event.interactable.snap({ anchors: [startPos] });
});
interact('.dropzone')
// dragenter listener stays the same...
.on('dragleave', function (event) {
// when leaving a dropzone, snap to the start position
event.draggable.snap({ anchors: [startPos] });
}) |
Oh wow. So cool! I need the block to always go back to the "toolbox drawer original position" if dropped outside of a dropzone. This works the at start, but as soon as you once drop it at a dropzone, then that dropzone becomes the "original place", which is not what I intended. The original position (home) should always be the "toolbox drawer", where all blocks are at start. |
Oh. In that case, you only need to set the properties of var startPos = null;
interact('.block')
.on('dragstart', function (event) {
if (!startPos) {
var rect = interact.getElementRect(event.target);
// record center point when starting the very first a drag
startPos = {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2
}
}
// snap to the start position
event.interactable.snap({ anchors: [startPos] });
}); |
Thanks once again Taye. I can go on with my little proof of concept now :) |
I'd be happy to help. Good luck! |
Hi taye - I have a couple of questions regarding your answers in this thread. Do you want me to ask them here or start my own? It's about how to implement the logic here with the new formatting (i.e. not using deprecated methods). |
@akwright I think it would be good to continue here, though I doubt that I'll have time to answer right now. |
No worries :) Whenever you get a chance would be fantastic! The first bit of code you provided seems to be working event.draggable.snap({
anchors: [ dropCenter ]
}); When I try to convert this based on the docs, it doesn't seem to be working, however I'm probably implementing it incorrectly: ondragenter: function (event) {
var draggableElement = event.relatedTarget,
dropzoneElement = event.target,
dropRect = interact.getElementRect(dropzoneElement),
dropCenter = {
x: dropRect.left + dropRect.width / 2,
y: dropRect.top + dropRect.height / 2
};
snap: {
anchors: [ dropCenter ]
};
... I don't see the anchors method listed in the snapping docs, so I guess just looking for clarification there. Also, when the draggable element snaps to the center of the droppable element, it's not always in the center. Sometimes it is, but it usually offsets itself by around 10px or so in any given direction if I re-drop it. |
Also, another issue is that when I set one of the snap points using the |
Sorry to keep bombarding you with questions, but is it possible to have both scenarios listed above, with the new syntax? Both the snapping to the drop zone when dropped and snap back to original start position if not dropped on a droppable zone? To give you some more context, here is a pen with my current code: http://codepen.io/akwright/pen/VYdNar |
I've kept the old // call the draggable method of the Interactable
// and give the snap options in the options object
interact(target).draggable({
snap: {
targets: [
{ x: 0, y: 0 }, // anchor
interact.createSnapGrid({ x: 25, y: 25 }), // grid
function (x, y) { return { x: x % 50, y: y % 50 }; } // path function
],
relativePoints: [ { x: 0.5, y: 0.5 } ] // instead of elementOrigin
// other snap settings...
}
});
You need to store the starting position of each element. The code that I provided is only meant for one element. I've forked your demo and fixed the snapping API usage so it should be a little easier to implement the rest of what you need: https://codepen.io/taye/pen/PwBPwb. Let me know if you'd like any more help :). |
@taye Thank you so much! I know that the snapping is in the docs, but for some reason I couldn't wrap my head around it. I have one more question. Feel free to tell me if it belongs in a new thread. I'm trying to determine when an already dropped draggable item is dragged off of the current drop zone. I have my dropzone interact('.droppable:not(.caught--it)').dropzone({
ondragleave: function (event) {
event.target.classList.remove('can--catch', 'caught--it');
event.relatedTarget.classList.remove('drop--me');
},
ondrop: function (event) {
event.target.classList.add('caught--it');
} I'm guessing it has something to do with calling the interact interact('.draggable').draggable({
onmove: function (event) {
event.dragLeave.classList.remove('caught--it');
} I know it says in the docs to add a listener to the event type - is that the same as when I declare it in the dropzone function above? |
Yeah, I think so. Similar to the
Yes, they're basically the same. The only difference is that the listeners are overwritten if you put them in the method options object and listeners added with the function listener1 (event) {}
function listener2 (event) {}
interactable.dropzone({ ondrop: listener1 });
interactable.ondrop === listener1; // true
interact.dropzone({ ondrop: listener2 });
interactable.ondrop === listener2; // true |
I think that makes sense :) I'll have to play around with it. I'm still not fully clear on the listeners and the methods, but again, I'll keep trucking! |
Hi Taye, I'm trying to to make the draggable element snapped back to its original position if it is not dropped on a dropzone, just like above. I try to implement the code from above and it does not works for me. Example code from above: var startPos = {x: 0, y: 0};
interact('.block')
.on('dragstart', function (event) {
var rect = interact.getElementRect(event.target);
// record center point when starting a drag
startPos.x = rect.left + rect.width / 2;
startPos.y = rect.top + rect.height / 2;
// snap to the start position
event.interactable.snap({ anchors: [startPos] });
});
interact('.dropzone')
// dragenter listener stays the same...
.on('dragleave', function (event) {
// when leaving a dropzone, snap to the start position
event.draggable.snap({ anchors: [startPos] });
}) Modified code (Not working) interact('.dropzone').dropzone({
// only accept elements matching this CSS selector.
accept: '.draggable',
// Listen for drop related events:
ondropactivate: function (event) {
// add active dropzone feedback.
event.target.classList.add('drop-active');
},
ondragenter: function (event) {
var draggableElement = event.relatedTarget,
dropzoneElement = event.target;
},
ondragleave: function (event) {
event.target.classList.remove('dropped');
// when leaving a dropzone, snap to the start position
event.draggable.snap({ anchors: [startPos] });
},
ondrop: function (event) {
event.target.classList.add('dropped');
// ****** This is to test the snapping back feature.
event.relatedTarget.isDropped = true;
}
if (scope.events.dropzone.ondrop) {
scope.events.dropzone.ondrop(event, scope.model);
}
},
ondropdeactivate: function (event) {
// remove active dropzone feedback
event.target.classList.remove('drop-active');
event.target.classList.remove('drop-target');
}
});
interact('.draggable').draggable({
//interact('#'+ cardModel.ticketNumber).draggable({
// enable inertial throwing
inertia: true,
//snap: {
// targets: [{x: 44, y: 237}],
// range: Infinity,
// relativePoints: [ { x: 0, y: 0 } ],
// endOnly: true
//},
onstart: function(event) {
var rect = interact.getElementRect(event.target);
startPos.x = rect.left + rect.width;
startPos.y = rect.top + rect.height;
// snap to the start position
event.interactable.snap({ anchors: [startPos] });
var cardIndex = event.target.dataset.index;
var columnIndex = event.target.parentNode.parentNode.parentNode.dataset.index;
var swimlaneIndex = event.target.parentNode.parentNode.parentNode.parentNode.parentNode.dataset.index;
event.target.cardIndex = parseInt(cardIndex);
event.target.columnIndex = parseInt(columnIndex);
event.target.swimlaneIndex = parseInt(swimlaneIndex);
// Add css class to make the z-index of the element being dragged to appear in front of other elements.
event.target.classList.add('card-placeholder-dragged');
},
// call this function on every dragmove event
onmove: function(event) {
dragMoveListener(event);
},
// call this function on every dragend event
onend: function (event) {
if (!event.target.isDropped) {
//event.interactable.snap({
// targets: [{x: 44, y: 237}]
//});
}
}
}); When I look at the browser console, it shows a warning that says "Interactable#snap is deprecated. See the new documentation for snapping at http://interactjs.io/docs/snapping" |
Hi, I think i'm getting closer. I manage to snap back to its original position but, the first time being dragged (any draggable element) outside of any dropzones after the page loaded won't snap. After the first time being dropped in the non-dropzone, the element now can be snapped back into its original position, however it cause another trouble which will skips my operations in the dropzone's 'ondrop' event when placed in any of the dropzones. I tested with some console.log in ondrop and discovered that it has been triggered at all when dropping onto the dropzone. Here's the updated code: var startPos = {x: 0, y: 0};
interact('.draggable').draggable({
inertia: true,
snap: {
//targets: [{x: 44, y: 237}],
range: Infinity,
relativePoints: [ { x: 0, y: 0 } ],
endOnly: true
},
onstart: function(event) {
event.target.isDropped = false;
var rect = interact.getElementRect(event.target);
startPos.x = rect.left; //+ rect.width;
//startPos.x = 300;
startPos.y = rect.top;// + rect.height;
//startPos.y = 300;
event.target.dropped = false;
// Send some info to change model.
var cardIndex = event.target.dataset.index;
var columnIndex = event.target.parentNode.parentNode.parentNode.dataset.index;
var swimlaneIndex = event.target.parentNode.parentNode.parentNode.parentNode.parentNode.dataset.index;
event.target.cardIndex = parseInt(cardIndex);
event.target.columnIndex = parseInt(columnIndex);
event.target.swimlaneIndex = parseInt(swimlaneIndex);
},
onend: function (event) {
if (event.target.dropped != true) {
// snap to the start position
event.interactable.draggable({
snap: {
targets: [startPos]
}
});
}
}
});
interact('.dropzone').dropzone({
// only accept elements matching this CSS selector.
accept: '.draggable',
ondrop: function (event) {
event.relatedTarget.dropped = true;
// Some change model operation here. (Which will be skipped when the bugs occurred????)
var cardIndex = event.relatedTarget.cardIndex;
var columnIndex = event.relatedTarget.columnIndex;
var swimlaneIndex = event.relatedTarget.swimlaneIndex;
var cardModel = scope.model.swimlanes[swimlaneIndex].columns[columnIndex].cards[cardIndex];
var targetColumnIndex = parseInt(event.target.dataset.index);
var targetSwimlaneIndex = parseInt(event.target.parentNode.parentNode.dataset.index);
if (columnIndex !== targetColumnIndex && swimlaneIndex === targetSwimlaneIndex) {
scope.model.swimlanes[swimlaneIndex].columns[columnIndex].removeCard(cardIndex);
scope.model.swimlanes[targetSwimlaneIndex].columns[targetColumnIndex].addCard(cardModel);
scope.$apply();
}
}
}); Why does it behave in such a way? Is there anything wrong with the snapping option that causes the ondrop event cannot be triggered? Thank you in advance. |
Hi, thank you for the code above. It helps me very much! To fix it, i thinked to create various id and change but the problem is that the number of elements changes, and not defined. Thank you very much! |
@loverdrive If you have multiple draggable elements then you'll need to store multiple start positions – one for each element. You can do this, for example, by storing the start positions in attributes of each element instead of in just one interact('.block')
.on('dragstart', function (event) {
// if the start position has not yet been saved
// in the element's data-start-x/y attributes
if (!event.target.hasAttribute('data-start-x')) {
var rect = interact.getElementRect(event.target);
// record center point when starting the very first a drag
event.target.setAttribute('data-start-x', rect.left + rect.width / 2);
event.target.setAttribute('data-start-y', rect.top + rect.height / 2);
}
... |
Ok, thank you very much! Now my code is: .on('dragstart', function (event) {
console.log({x: event.target.getAttribute('data-start-x'), y: event.target.getAttribute('data-start-y')});
if (!event.target.hasAttribute('data-start-x')) {
var rect = interact.getElementRect(event.target);
// record center point when starting the very first a drag
event.target.setAttribute('data-start-x', rect.left + rect.width / 2);
event.target.setAttribute('data-start-y', rect.top + rect.height / 2);
}
// snap to the start position
event.interactable.snap({ anchors: [{x: event.target.getAttribute('data-start-x'), y: event.target.getAttribute('data-start-y')}] });
}) The problem is that the draggable elements snap only a little (in an undefined and variable position), not in the original position. I use console.log to see the value of data-start-x/y and they are correct. So i think the problem is in the last line: event.interactable.snap({ anchors: [{x: event.target.getAttribute('data-start-x'), y: event.target.getAttribute('data-start-y')}] }); I don't know how to solve. Could you help me? |
@loverdrive you should use the new snapping API described in the docs. I've explained the difference in a comment above. |
Ok, i ask you confirm because i'm not sure to understand the changing API. So, i need to change the line event.interactable.snap({ anchors: [{x: event.target.getAttribute('data-start-x'), y: event.target.getAttribute('data-start-y')}] }); with snap: {
targets: [
{ x: event.target.getAttribute('data-start-x'), y: event.target.getAttribute('data-start-y')}
]
} ? What about the previous block to code i needed to write? This: .snap({
mode: 'anchor',
anchors: [],
range: Infinity,
elementOrigin: { x: 0.5, y: 0.5 },
endOnly: true
}); I need to delete it? Thank you! EDIT: No, changing the last line with snap: {
targets: [
{ x: event.target.getAttribute('data-start-x'), y: event.target.getAttribute('data-start-y')}
]
} doesn't work. It doesn't return at any start position (no snapping). What i should insert to make snapping at start position working? EDIT 2: I don't understand how to use the new snap API. .snap({
mode: 'anchor',
anchors: [],
range: Infinity,
elementOrigin: { x: 0.5, y: 0.5 },
endOnly: true and some line like that: event.draggable.snap({ anchors: [startPos] }); or event.interactable.snap({ anchors: [startPos] }); I don't understand how convert it all to new API. Could you explain it, with example of code? |
Ok, I solved. I change to new API. interact('.draggable').draggable({
snap: {
targets: [startPos],
range: Infinity,
relativePoints: [ { x: 0.5, y: 0.5 } ],
endOnly: true
},
onstart: function (event) {
var rect = interact.getElementRect(event.target);
// record center point when starting the very first a drag
startPos = {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2
}
event.interactable.draggable({
snap: {
targets: [startPos]
}
});
},
})
// !!!!!!! DROPZONE !!!!!!!
interact('.dropzone').dropzone({
ondragenter: function (event) { // when element enter in the dropzone (and has the possibility of a drop)
var draggableElement = event.relatedTarget,
dropzoneElement = event.target,
dropRect = interact.getElementRect(dropzoneElement),
dropCenter = {
x: dropRect.left + dropRect.width / 2,
y: dropRect.top + dropRect.height / 2
};
event.draggable.draggable({
snap: {
targets: [dropCenter]
}
});
},
ondragleave: function (event) { // when element is dragged out from dropzone
event.draggable.draggable({
snap: {
targets: [startPos]
}
});
},
}) The problem is that when i move the draggable element out the dropzone (onleave events), it return into dropzone, not at the start position. So, i tried to save the original position, in order to read the variable written in .draggable also in .dropzone. event.target.setAttribute('data-start-x', rect.left + rect.width / 2);
event.target.setAttribute('data-start-y', rect.top + rect.height / 2); after starPos declaration. So, in .dropzone onleave i added startPos = {
x: event.target.getAttribute('data-start-x'),
y: event.target.getAttribute('data-start-y'),
} Now draggable element snap of few pixel, but apparently in a random snap. It doesn't snap into startPos, neither in the dropzone. @taye , could you help me to solve this final problem? Thank you very much! |
UPDATE: I solved, but i think there's a bug. I save start position in the attribute of the object (event.target.data-start-x). Then i call snap function passing it the values stored in the object attribute (see my examples above), and the element snapping to a undefined position, not to the coordinates point. I print the values from the attribute with an alert() or console.log, and they are correct, but snapping is not working correctly. Then i saved the starting position into an external json instead of the element attribute, and it worked perfectly!! |
The problem is probably that the |
Hello I am trying to achieve the same as you loverdrive, could yo post the final javascript file? Thanks! |
@loverdrive |
Hi Johannes, if I can help in any way I'd be glad to. I got this working in Cheers, On Wednesday, August 31, 2016, Johannes Eschrig [email protected]
|
Thanks! I got it working after all, I was missing the after adding this it works now. |
Cool. Glad to hear it! On Wednesday, August 31, 2016, Johannes Eschrig [email protected]
|
Hi, I've been trying to work on a lil UI feature with Interact for a couple days now, and this thread has been extremely helpful with dropzones and snapping. Thanks :) I am having a problem with my implementation that I don't quite understand the solution for and I hope someone can help me out. Basically what I want is for, after something from one dropzone is moved into a different dropzone, whatever is currently in that second dropzone then snaps to the first. My code is below.
The snap updates on the correct interact object but I assume that I need to somehow cause the interact object to hear a 'move' event so it can actually snap to where it should be, and I don't actually understand how to make that happen. I was playing around with Interactable.fire() and trying to set up my own iEvents but I couldn't get it working. Tips? Thanks! |
Hi, https://jsfiddle.net/rub6qud5/110/ w interact integrated. output window is large, needs to be stretched all Have a look at the project, the code, if it seems theres anything useful On Tue, Oct 18, 2016 at 3:55 PM, jkae [email protected] wrote:
|
Anyone can help me out by posting the working code to achieve same functionality. |
hi gvadl,
I recently made some tweaks to the project i was using interact.js for.
You can see the project here:
- https://codepen.io/jlazar/pen/RpWBvB
interact object/code is at line 1086-1159
I can also send you my github w the project if you want.
its been about a yr since i wrote this, but happy to try and answer any
questions if i can
…On Mon, Mar 6, 2017 at 7:47 AM, gvadl ***@***.***> wrote:
Anyone can help me out by posting the working code to achieve same
functionality.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#79 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGjy1gxHiJj-GBgu-KYNCRZF1FWFBvo3ks5rjABogaJpZM4CkcWr>
.
|
Hi, I have read all above and i encounter some issue with event.draggable.snap which is not working inside ondrop function. ondrop: function (event) { } Please help me to guide which is missing. Thanks. |
@zacKyawMT I think the answer to your question lies in the CodePen that @taye fixed and in this comment: #79 (comment) If I understand this well enough, the event.interactable.draggable({
snap: { anchors: [ startPos ] }
}) or maybe event.draggable.draggable({
snap: { targets: [ startPos ] }
}) depending on where this code lies and what you're trying to do? |
Hi all, I can't get this working. I'm trying to move the dragged item back to original position after its dropped (anywhere),
I've tried several of the proposed solutions but can't figure it out. Any help is appreciated. |
Hi all, I have an alternative answer to the various duplicates of this that ask about returning the dragged item back to its original position (most recently asked by @jpaoletti above). I was looking for a solution that would reset the dragged item to its starting position on a button click. I was unable to implement any of the solutions presented so far, but I think the solution I did come up with may be useful for others. Note this solution is a hack; it overrides internal values and doesn't have an animation, just jumps the element back to its original spot This is an example (from my browser's dev tools) of an element that has been dragged and dropped in a new position. The bottom line is most important. You can see that it has a css Therefore, if you want to move the element back to its starting position, you change this property. However, as soon as you pick it up again the element jumps back to the position it was at previously. So the solution in jquery to place an element at its original position is:
This code could be put in a function and used wherever it's needed. P.S: If an element has |
Hello everyone! I have a similar issue to some of the ones described in this thread, but I haven't found an exact match to my problem. I want, in essence, a "Reset" button that moves all my draggable objects to their starting position. Has anyone implemented this? Thanks in advance and have a great day. |
Hello,
I'm sorry this is not a bug, just a question. I'm not sure where to ask this...
I'm building a little "product builder tool" for a client, using interact.js. I just started testing it, hoping I can have all the functions I need with it.
I have a bunch of dropzones, letting dragable boxes be able to be dropped on these dropzones. The boxes should be snapped/centered (with animation) when dropped onto the dropzone.
And also the "element origin" should be centered to the dragable box, so that the dropzone gets active once the center of the dragable box is on the drop zone, and not the cursor (as now).
Are these two things possible to achieve with intereact.js?
Have a look here to see what I'm trying to do:
http://kundprojekt.soonce.com/lintex/zonbuilder/
The text was updated successfully, but these errors were encountered: