Next page | Contents page |

Click or touch a card: targets

We will add an event listener to the canvas in order to respond to mouse clicks or, perhaps on a phone, a touch. The event will give us the coordinates of the click or touch. Put this function in gameA.js as a useful general method of getting the coordinates from any of the mouse events:


function getMousePoint (event)
{ return { x: event.clientX + document.body.scrollLeft + 
            document.documentElement.scrollLeft - 
			GAME.cnv.offsetLeft,
           y: event.clientY + document.body.scrollTop  + 
            document.documentElement.scrollTop  - 
			GAME.cnv.offsetTop };
}

For beginners: when a user does something, such as click the mouse or press a key, the operating system detects that and passes information about the event to the browser. The browser creates a JS object of type Event (see mdn event) and invokes our program by calling our listener function with the event object as its parameter. Our program may have finished doing anything, with the last drawing of the cards. Now it starts running again, from the listener function. If instead our program is still running, the event objects are queued by the browser until they can be received by our program. It is important to understand that this is how the JS system works. It is event-driven.

If a click occurs we want to find out not just the coordinates but which card was clicked (or touched). That requires defining areas of the canvas as targets. It would be possible to do that using a Path2D object in the 2D canvas API but I decided to make something I could more easily tailor to my requirements, so make way for another object type: Target. Create the usual .js file and make a script element for it in the HTML file. Here are the contents:


function Target (xLeft, yTop, xRight, yBottom, 
                 column, card, cardIndexInColumn)
{ this.xL = xLeft;
  this.yT = yTop;
  this.xR = xRight;
  this.yB = yBottom;
  this.column = column;
  this.card = card;
  this.index = cardIndexInColumn;
}

Target.prototype.contains = function (x, y)
{ return (x >= this.xL && x <= this.xR
       && y >= this.yT && y <= this.yB);
};

function findTarget (x, y, targets)
{ for (const T of targets)
  { if (T.contains (x, y)) return T;
  }
  return null; // (x, y) not inside any target
}

Target.contains() tests whether the point (x, y) lies within the target rectangle. The function findTarget() implies that the game is keeping a list of possible targets. So we will need some code for that elsewhere.

At the start of a game we need


	GAME.targets = [];

That array has to be populated every time the canvas is drawn. It can be done in Column.draw() which gets 2 new statements:


Column.prototype.draw = function ()
{ for (var i = 0, y = this.YT; 
       i < this.CARDS.length; i++, y += this.DY)
  { var card = this.CARDS [i];
    card.draw (this.XL, y);
    var targetHt = 
	    (i === this.CARDS.length - 1) ? card.height : this.DY;
    GAME.targets.push (new Target (this.XL, y, this.XL + card.width, 
	                               y + targetHt, this, card, i));
} };

The target height depends on whether the target is the bottom card of the column, fully exposed, or one higher up that is overlapped by another.

Next page | Contents page |