Canvas 2D Web Apps/Static Buttons
This chapter discusses the most basic way to implement buttons with cui2d. It is certainly not the only way to implement buttons, but it is very flexible and the code to define the appearance and function of a button is a simple if
statement that checks whether the user event has been occurred in the region of the button and specifies how to react to such events. In this chapter, the appearance of buttons is static; buttons that change their appearance based on user events (e.g., a hovering mouse pointer) are discussed in the chapter on responsive buttons.
An Example: Selecting a Color
editThe example of this chapter shows three buttons labeled “red,” “green” and “blue,” as well as a color patch, which changes its color according to the clicked button. The buttons are rendered with a bitmap image which allows for arbitrary button designs. The example is available online, and a version that can be downloaded to mobile devices is also available online. If you want to try out the example on your local computer, you should also download the files cui2d.js
and the image selected.png
to the same directory as the HTML file.
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no">
<script>
function init() {
// get image
imageFocusedButton.src = "selected.png";
imageFocusedButton.onload = cuiRepaint;
// deactivate single-finger dragging for myPage
myPage.interactionBits = cuiConstants.isTransformableWithTwoFingers;
// set defaults for all pages
cuiBackgroundFillStyle = "#000000";
cuiDefaultFont = "bold 20px Helvetica, sans-serif";
cuiDefaultFillStyle = "#FFFFFF";
// initialize cui2d and start with myPage
cuiInit(myPage);
}
// create an image for the button
var imageFocusedButton = new Image();
// create a color
var myColor = "#000000";
// create a new page of size 400x300 and attach myPageProcess
var myPage = new cuiPage(400, 300, myPageProcess);
// a function to repaint the canvas and return false (if null == event)
// or to process user events (if null != event) and return true
// if the event has been processed
function myPageProcess(event) {
// draw and react to buttons
if (cuiIsInsideRectangle(event, 50, 50, 80, 50, "red", imageFocusedButton)) {
if ("mouseup" == event.type || "touchend" == event.type) {
myColor = "#FF0000";
cuiRepaint();
return true;
}
}
if (cuiIsInsideRectangle(event, 150, 50, 80, 50, "green", imageFocusedButton)) {
if ("mouseup" == event.type || "touchend" == event.type) {
myColor = "#00FF00";
cuiRepaint();
return true;
}
}
if (cuiIsInsideRectangle(event, 250, 50, 80, 50, "blue", imageFocusedButton)) {
if ("mouseup" == event.type || "touchend" == event.type) {
myColor = "#0000FF";
cuiRepaint();
return true;
}
}
// click on background?
if (null != event) {
if ("mouseup" == event.type || "touchend" == event.type) {
myColor = "#000000";
cuiRepaint();
return true;
}
}
// repaint this page?
if (null == event) {
// draw color box
cuiContext.fillStyle = myColor;
cuiContext.fillRect(150, 150, 80, 80);
// background
cuiContext.fillStyle = "#404040";
cuiContext.fillRect(0, 0, this.width, this.height);
}
return false; // event has not been processed
}
</script>
</head>
<body bgcolor="#000000" onload="init()"
style="-webkit-user-drag:none; -webkit-user-select:none; ">
<span style="color:white;">A canvas element cannot be displayed.</span>
</body>
</html>
Discussion
editThis discussion assumes that you are familiar with the code example discussed in Chapter Getting Started with the Framework.
The example uses a bitmap image to draw a button. The image is assigned to the global variable imageFocusedButton
:
// create an image for the button
var imageFocusedButton = new Image();
The image file is then specified in the init()
function:
// get image
imageFocusedButton.src = "selected.png";
imageFocusedButton.onload = cuiRepaint;
By setting onload
to cuiRepaint
, we can make sure that the canvas is repainted in case it has been rendered before the image was completely loaded.
The example defines a global variable myColor
to store the user-selected color:
// create a color
var myColor = "#000000";
Since the example uses clicks onto the background to set myColor
to black, these “one-finger” events should not be processed by the page. Thus, the example deactivates one-finger dragging by only allowing two-finger gestures for this page:
// deactivate single-finger dragging for myPage
myPage.interactionBits = cuiConstants.isTransformableWithTwoFingers;
For repainting the buttons and processing clicks on the buttons, the example uses three calls to cuiIsInsideRectangle()
:
// draw and react to buttons
if (cuiIsInsideRectangle(event, 50, 50, 80, 50, "red", imageFocusedButton)) {
if ("mouseup" == event.type || "touchend" == event.type) {
myColor = "#FF0000";
cuiRepaint();
return true;
}
}
if (cuiIsInsideRectangle(event, 150, 50, 80, 50, "green", imageFocusedButton)) {
if ("mouseup" == event.type || "touchend" == event.type) {
myColor = "#00FF00";
cuiRepaint();
return true;
}
}
if (cuiIsInsideRectangle(event, 250, 50, 80, 50, "blue", imageFocusedButton)) {
if ("mouseup" == event.type || "touchend" == event.type) {
myColor = "#0000FF";
cuiRepaint();
return true;
}
}
The definition of cuiIsInsideRectangle()
is discussed below. Here, it is only important that it works similarly to a process function:
- If
event
isnull
,cuiIsInsideRectangle()
writes a string ("red"
,"green"
, or"blue"
) in the specified rectangle and then drawsimageFocusedButton
under it. - If
event
is notnull
, it checks whether this user event is inside the specified rectangle and returnstrue
if it is. In this case, the type of the event is checked, and in case ofmouseup
andtouchend
events,myColor
is changed to some color (depending on the button) and a repaint is requested withcuiRepaint()
to make sure that the new color is used for drawing. Also, the function returns withtrue
to indicate that the event has been processed.
The following code checks whether there was a mouseup
or touchend
anywhere else, and in this case it sets myColor
to black:
// click on background?
if (null != event) {
if ("mouseup" == event.type || "touchend" == event.type) {
myColor = "#000000";
cuiRepaint();
return true;
}
}
Lastly, the function checks whether event
is null
and in that case it repaints a square rectangle with the color myColor
and a grey background:
// repaint this page?
if (null == event) {
// draw color box
cuiContext.fillStyle = myColor;
cuiContext.fillRect(150, 150, 80, 80);
// background
cuiContext.fillStyle = "#404040";
cuiContext.fillRect(0, 0, this.width, this.height);
}
The code then returns false
to indicate that the event hasn't been processed.
When you try out the example, you might realize that it feels very static even though you can change the color by clicking on the buttons and the background. The example in Chapter Responsive Buttons feels a lot more dynamic because the appearance of the buttons responds to user events.
Implementation of cuiIsInsideRectangle()
editThe function cuiIsInsideRectangle()
is defined in cui2d.js
:
/**
* Either determine whether the event's position is inside a rectangle (if event != null)
* or draw an image in the rectangle with a text string on top of it (if event == null).
*/
function cuiIsInsideRectangle(event, x, y, width, height, text, image) {
if (null == event) { // draw button
if (null != text) {
cuiContext.fillText(text, x + width / 2, y + height / 2);
}
if (null != image) {
cuiContext.drawImage(image, x, y, width, height);
}
return false;
}
else { // if (null != event)
if (event.eventX >= x && event.eventX < x + width &&
event.eventY >= y && event.eventY < y + height) {
return true;
}
return false;
}
}
The implementation is very straightforward: if event
is null
, the text
is written at the center of the rectangle unless the text
is null
. Similarly, image
is drawn into the rectangle unless it is null
. On the other hand (if event
is not null
), the function checks whether the coordinates eventX
and eventY
in event are inside the rectangle and returns the result.
Note that eventX
and eventY
are specific to cui2d: these coordinates are in the coordinate system of the page, which is different from any coordinate system that HTML is using and it changes if the user transforms the page with two-finger gestures.