DHTML Jigsaw Puzzle: IE4; Positioning Pieces | WebReference

DHTML Jigsaw Puzzle: IE4; Positioning Pieces

Logo

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 nulled 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