| Animation by Example: Dragging
Images by David Matuszek |
In this applet you can grab either the skull or the skeleton and move it around. The skull can be moved anywhere within the applet; the skeleton can be moved horizontally, but not vertically.
To do this, we need to make use of two separate kinds of listeners, MouseListener
and MouseMotionListener. The first of these, MouseListener,
is used to tell us when the mouse button is pressed and when it is released.
The second, MouseMotionListener, is used to tell us when the mouse
is moved with the mouse button held down.
To begin, we need two variables to help us keep track of what is going on:
boolean draggingSkull = false; boolean draggingSkeleton = false;
Next we need to attach a MouseListener. Listeners can only be
attached to Components, and our images are not Components. In our examples we
have a class View that extends Canvas, and Canvas
is a Component, so we attach our listeners to the object view of
class View.
When the mouse button is pressed inside the View, we check first
whether the mouse is over the skull. This is a pretty sloppy check, because
the skull is within a 100x100 pixel square, most of which is transparent; the
actual skull is really only about 50x50. However, since the skull is moving
rapidly, we don't want to make it too hard to catch. If the mouse is
not over the skull, then we check whether it is over the skeleton; we
don't want to move both images at once (although we could, if that was what
we wanted to do).
When the mouse button is released, regardless of where the mouse is, we turn off dragging for both the skull and the skeleton. If they weren't being dragged, no harm is done.
// We need a MouseListener in order to start and stop dragging
view.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent event) {
// Get mouse position
int mouseX = event.getX();
int mouseY = event.getY();
// Check whether the mouse is inside the skull
if (mouseX >= model.xPosition &&
mouseX <= model.xPosition + model.skullWidth &&
mouseY >= model.yPosition &&
mouseY <= model.yPosition + model.skullHeight) {
draggingSkull = true;
}
// Check whether the mouse is inside the skeleton
// (but NOT inside the skull, since we only want to
// drag one thing at a time)
else if (mouseX >= model.xSkeleton &&
mouseX <= model.xSkeleton + model.skeletonWidth &&
mouseY >= model.ySkeleton &&
mouseY <= model.ySkeleton + model.skeletonHeight) {
draggingSkeleton = true;
}
}
public void mouseReleased(MouseEvent event) {
draggingSkull = false;
draggingSkeleton = false;
}
});
All we have done so far is control the setting of two variables, draggingSkull
and draggingSkeleton. However, that's the hard part. When the mouse
is being moved with a mouse button down, all we have to do is reposition the
skull or the skeleton to be under the mouse.
// Drag an image with the mouse
view.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent event) {
// Get mouse position
int mouseX = event.getX();
int mouseY = event.getY();
if (draggingSkull) {
// Center skull over mouse position
model.xPosition = mouseX - (model.skullWidth / 2);
model.yPosition = mouseY - (model.skullHeight / 2);
}
if (draggingSkeleton) {
// Center skeleton over horizontal mouse position
model.xSkeleton = mouseX - (model.skeletonWidth / 2);
}
}
});
There are two minor problems with this code, neither of which I'm going to bother to correct.
First, when the mouse is clicked over any part of an image, the image "jumps" to center itself over the mouse pointer. With a little arithmetic, we could note exactly where in the image the mouse was clicked, and keep that part of the image under the mouse pointer. However, it is arguable whether this is "better." Besides, in the present example, things are moving around so much that you can hardly notice the small jump.
Second, the skull and skeleton are moving. We only get mouseDragged
events when the mouse is moving (with the button down), so if we stop
moving the mouse, the image we are dragging will move away, only to snap back
the next time we move the mouse. This could easily be fixed by adding a couple
of lines to the Model:
if(!Controller.draggingSkull) { ...move skull... }
if (!Controller.draggingSkeleton) { ...move skeleton... }
|
|
|