# DHTML Jigsaw Puzzle: IE4; Positioning Pieces

## The DHTML Lab Jigsaw Puzzle, Part II: IE4 cont'd

*moving* the puzzle pieces into position

### Dropping the Puzzle Piece

Our old `dropEl()` function was simple. It just stated that there no
longer was an element to be dragged:

- function dropEl() {
whichEl == null;
}

The puzzle scenario, however, introduces several other considerations:

- has the piece been dropped over the puzzle area?
- if yes, which row/column is it closest to?
- when found, snap it into position
- is it in the correct position?
- if yes, alert the user to a score!

`dropEl()` is a fairly long function. Here it is, complete, for
reference. We will go through it, line by
line, immediately following.

- function dropEl() {
if (whichEl == null) { return };
if (whichEl == elPuzzle) { whichEl = null; return };
dropLeft = event.clientX + document.body.scrollLeft;
dropTop = event.clientY + document.body.scrollTop;
allowLeft = puzzLeft;
allowRight = puzzLeft + puzzWidth;
allowTop = puzzTop;
allowBot = puzzTop + puzzHeight;
if (dropLeft >= allowLeft && dropLeft = allowTop && dropTop pieceWidth/2) {
if (modL > 0) {whereL += pieceWidth} else {whereL -= pieceWidth}
}
if (Math.abs(modT) > pieceHeight/2) {
if (modT > 0) {whereT += pieceHeight} else {whereT -= pieceHeight}
}
whichEl.style.pixelLeft = puzzLeft - whereL;
whichEl.style.pixelTop = puzzTop - whereT;
if (whichEl.style.pixelLeft == puzzLeft && whichEl.style.pixelTop == puzzTop) {
tempEl = whichEl;
flashTimer = setInterval("visToggle(false)",100);
}
}
whichEl=null;
}

Since `dropEl()` is called on every instance of `mouseup`, it first
checks to see if a drag was in progress. If `whichEl` has a value other
than `null`, an element was being dragged, so the function continues, otherwise the function returns:

- if (whichEl == null) { return };

Next, it determines if the full puzzle was the dragged element. If it is,
`dropIt()` ends the drag and returns. The remainder of the function
applies only if a puzzle piece was being dragged.

- if (whichEl == elPuzzle) { whichEl = null; return };

We now assign values to several variables. The page mouse position on
the `mouseup` event is assigned to `dropLeft` and `dropTop`:

- dropLeft = event.clientX + document.body.scrollLeft;
dropTop = event.clientY + document.body.scrollTop;

The next four
variables store the left, right, top and bottom positions of the puzzle. These variables, `allowLeft`, `allowRight`, `allowTop` and `allowBot`
help us determine if the puzzle piece was dragged onto the puzzle or
just to an irrelevant position on the page:

- allowLeft = puzzLeft;
allowRight = puzzLeft + puzzWidth;
allowTop = puzzTop;
allowBot = puzzTop + puzzHeight;

If the user generated the `mouseup` event within the puzzle area, a
series of statements are executed, and then `whichEl` is given a value of `null`.
If not, the statements are skipped, and `whichEl` is `null`ed immediately:

- if (dropLeft >= allowLeft && dropLeft <= allowRight &&
dropTop >= allowTop && dropTop <= allowBot) {
.
.
.
}
whichEl = null;

### Snapping the Piece Into Position

Once we have determined that the puzzle piece was dropped in the
puzzle area, we must calculate which row and column coordinates are the
closest to the dropped piece, so we can *snap* the piece into position.
First, we find the difference between the puzzle left/top and the
piece left/top. Always bear in mind that the piece coordinates are those
of the *unclipped* image.

- diffLeft = puzzLeft - whichEl.style.pixelLeft;
diffTop = puzzTop - whichEl.style.pixelTop;

For example, if our puzzle left is `200`, and our piece left is `110`, the
difference (`diffLeft`) will be 90 pixels. We next divide this difference
by the width of the piece. If our piece was 40 pixels wide, our result
would be "2.25". With the `parseInt()` method, we keep only the full piece
count (2) and lose the quarter. We now know that the difference between the
puzzle left and the piece left is two full pieces. If we multiply this
by the pixel width of the piece (`pieceWidth`) we get 80 pixels. We assign
this value to a variable, `whereL`. The same procedure is followed for the
top difference.

- whereL = parseInt( diffLeft / pieceWidth ) * pieceWidth;
whereT = parseInt( diffTop / pieceHeight ) * pieceHeight;

Next, we get the *modulo* of the division we performed above. That is,
we once again divide 90 by 40, but this time we keep the pixel count that
*remains after our full piece count*. This is 10. We assign the left and
top modulo to `modL` and `modT`. What do we know so far? We know that if we
adjusted our left pixel position by 10, we would have our *snap*,
since we are 10 pixels off the full piece left coordinate.

- modL = parseInt( diffLeft % pieceWidth );
modT = parseInt( diffTop % pieceHeight );

If we stopped our calculations here, we would have our pieces always snap to the top-left even if they were closer to another row or column, to the right or bottom. We should check to see if the modulo, these extra pixels, are more than half of the width/height of a piece. If one or both are, we should make an adjustment. We should snap to the right, or bottom, or both.

- if (Math.abs(modL) > pieceWidth/2) {
if (modL > 0) {whereL += pieceWidth} else {whereL -= pieceWidth}
}
if (Math.abs(modT) > pieceHeight/2) {
if (modT > 0) {whereT += pieceHeight} else {whereT -= pieceHeight}
}

If our piece left, when dropped, was larger than our puzzle left, all
the calculations we have done above would be in negative numbers,
including the modulo. In our running example, a `modL` value of `10` or `-10`
tell us the same thing. We are 10 pixels off the target. So we call upon
`Math.abs()` to use the *absolute value* of `modL`, which is always positive.
We compare this to `pieceWidth` to establish if our extra pixels are more
than half of the width. If they are, we change `whereL` to reflect the
position of the next full piece. If `modL` is positive, `whereL` is
incremented by a full piece width. If it is negative, it is
decremented.

Now we position the dropped piece in the nearest slot:

- whichEl.style.pixelLeft = puzzLeft - whereL;
whichEl.style.pixelTop = puzzTop - whereT;

If our piece has been positioned in the correct solve position, its top and left coordinates will be the same as the puzzle's.

- if (whichEl.style.pixelLeft == puzzLeft && whichEl.style.pixelTop == puzzTop) {
tempEl = whichEl;
flashTimer = setInterval("visToggle(false)",100);
}

If they are, we assign the piece to our working variable `tempEl`, and call `visToggle()` to flash the piece, alerting the user to success. More on `visToggle()` later.

The above routine may have confused some. It works, as you know, but take it step by step and experiment with the numbers. It will make sense, guaranteed.

Now that we know what happens when the puzzle is played by a user, let's look at solving it automatically.

Produced by Peter Belesis and

All Rights Reserved. Legal Notices.Created: Nov. 13, 1997

Revised: Jan. 18, 1998

URL: http://www.webreference.com/dhtml/column9/puzzDrop.html