In Animate CC, and using the CreateJS/EaselJS API, I created a considerable amount of different HTML5 canvas, all of them featuring drag and drop elements, each time with progressive features.
To build the drag and drop interaction, I added to a Drag MovieClip some EventListeners, on mousedown
, pressmove
and pressup
events. All of them have associated functions.
/* this refers to the Drag MovieClip */
root = this;
/* Mousedown Events */
/* removingFromDrop: Detects when a Drag is removed from certain Drop */
root.on("mousedown", removingFromDrop);
/* Pressmove Events */
/* onPressMove: Moves the Drag on pressmove */
root.on("pressmove", onPressMove);
/* detectDrop: While pressmoving, detects if the Drag is over a Drop */
root.on("pressmove", detectDrop);
/* Pressup events */
/* setDragOverDrop: Releasing the left button of the mouse,
if a Drag is over a Drop, the Drag is positioned over the Drop */
root.on("pressup", setDragOverDrop);
As I said, all the drag and drop features were implemented in a progressive way, so in some canvas the drags doesn't detect the drops (intentionally), in others a transparency (alpha) is applied to the drags when they are over a drop, in others the drag must be put over a drop or return to their original position, in others the drag when is put over a drop, it can't be dropped to other positions; etc.
What doesn't change in almost all of this implementations is the way the drag moves on the pressmove
event...
function onPressMove(event) {
// There is more code here, but for effects
// of this question, it's irrelevant
/* In layer terms, drag must be over the drop */
root.parent.setChildIndex(event.currentTarget, root.parent.numChildren-1);
/* Drag takes the actual position of the mouse pointer */
var point = stage.globalToLocal(event.stageX, event.stageY);
event.currentTarget.x = point.x;
event.currentTarget.y = point.y;
}
...and how it detects a drop when it is below, also on the pressmove
event.
function detectDrop(event){
/* The original function is more complex than this. For example, the drop detection
in the original function is being done against all the available drops.
For effects of this question, I'm simplyfying this function, doing the detection
over a single drop */
/* Initial variables */
var dropLocation; // Drop Location
var point; // Point for the collision comparison
var dropFound = false; // Drop is found? By default, no.
/* Drop is cloned. For effects of this question, the drop is a global variable. */
dropLocation = drop.clone();
/* Position of the drop is converted to local using the mouse pointer position */
point = dropLocation.globalToLocal(event.stageX, event.stageY);
/* In local coordinates, the drag intersects with the point defined before? */
if (event.currentTarget.hitTest(point.x,point.y)) {
/* If yes, the drop was found */
dropFound = true;
}
if (dropFound) {
// Irrelevant for effects of this question
}
}
There is only one case scenario when the drag and drop interaction doesn't work as expected, and that case is when the canvas is made responsive.
There is no problem when the canvas is positioned in the center, both the drag movement and the drop detection works fine.
But when the canvas is made responsive, the movement of the drag works fine, but a drop is not detected when a drag is over it. As I have tested until now, the drag detects a certain drop, in another position, which is obviously not the position of the previously mentioned drop.
After some tests I was doing to drags/drops similar to the core implementation shown above, I've came to a solution that involves additional transformations on the drop detecting function.
First, the point variable now holds the current mouse position within the coordinate space of the stage:
var point = stage.globalToLocal(event.stageX, event.stageY);
Then, I use that point to transform to the coordinate space of the drop. That transformation is being held in another point variable:
var pointGTL = dropLocation.globalToLocal(point.x, point.y);
The intersection test in local coordinates, is being done with the point variable defined before:
event.currentTarget.hitTest(pointGTL.x,pointGTL.y)
Now the drop detecting function looks like this:
function detectDrop(event){
/* Initial variables */
var dropLocation; // Drop Location
var point, pointGTL; // Points for the collision comparison
var dropFound = false; // Drop is found? By default, no.
/* Drop is cloned */
dropLocation = drop.clone();
/* Position of the stage is converted to local
using the mouse pointer position */
point = stage.globalToLocal(event.stageX, event.stageY);
/* Then, the position of the drop is converted to local
using the point defined before */
pointGTL = dropLocation.globalToLocal(point.x, point.y);
/* In local coordinates, the drag intersects with the point defined before? */
if (event.currentTarget.hitTest(pointGTL.x,pointGTL.y)) {
/* If yes, the drop was found */
dropFound = true;
}
/* Rest of the code */
}
The drags works fine in both responsive modes (Fit in view or Stretch to fit).