iPhone Multi-touch Canvas

Awhile back I was curious about handling multi-touch stuff on the canvas element on safari mobile. This is pretty easy with the use of touchevents. Here is a quick video of me drawing on the canvas element in safari mobile on the iPhone:

If you’d like to try it yourself, you can go to this link

If you’re not on your iPhone it will just draw with the mouse.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<!DOCTYPE html> 
<html lang="en"> 
  <head> 
    <meta charset="utf-8" />
    <meta name="viewport" 
    content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />
    <title>Drawing</title>
    <style> 
      html, body{
        margin : 0;
        padding : 0;
        background-color : white;
        color : white;
        font-family : sans-serif;
        font-size : 12px;
        overflow : hidden;
      } 
      div{
        margin: 10px; 
      }
      .note{
        z-index: 100;
        position : absolute;
        left : 0px;
        height : 0px;
      }
      a{
        color : white;
        font-weight : bold; 
      }
    </style> 
  </head> 
  <body> 
    <div class="note">Draw on me</div>
    <canvas id="canvas" width ="400" height="400">
      <div>
        Your browser does not support the canvas element.
        I recommend you 
        <a href="http://www.google.com/chrome?hl=en" target="_blank">
          Download Google Chrome
        </a>
      </div>
    </canvas> 
 
    <script> 
      // hide iphone tool bar
      window.onload = function() {
        setTimeout(function(){
           window.scrollTo(0, 1);
        }, 100);
      }
 
      var canvas = document.getElementById("canvas");
      var c = canvas.getContext("2d");
      var TWO_PI = Math.PI * 2;
 
      function clear(){
        c.fillStyle = "black";
        c.fillRect(0, 0, canvas.width, canvas.height);
      }
 
      function dot(x, y){
        c.fillStyle = "rgba(255,255,255,0.3)";
        c.beginPath();
        c.arc(x, y, 10, 0, TWO_PI, true);
        c.closePath();
        c.fill();
      }
 
      // if it's an iphone, 480x480 is a good size for
      // both orientations
      if (navigator.platform == "iPhone"){
        canvas.width = 480;
        canvas.height = 480;
      }else{
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        // no need for this on the iphone
        window.onresize = function(){
          canvas.width = window.innerWidth;
          canvas.height = window.innerHeight;
          // resizing clears the browser window
          clear();
        }
      }
      clear();
 
      // for regular browsers
      document.onmousemove = function(e){
        dot(e.clientX, e.clientY);
      }
 
      // for iphone:
      document.ontouchmove = function(e) {
        // prevent scaling and sliding
        e.preventDefault();
        // draw dots at each touch point
        var leng = e.touches.length;
        for (var i = 0; i < leng; i++) {
          dot(e.touches[i].pageX, e.touches[i].pageY);
        }
      }
 
      window.onorientationchange = function() {
        // erase when the phone is rotated
        // clear();
        window.scrollTo(0, 1);
      }
    </script> 
  </body> 
</html>

So the first important thing here is this code:

<meta name="viewport" 
content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />

Which tells safari to size the content to the size of the device and prevent scaling. This will be ignored by normal browsers like chrome, firefox etc…

The next important thing is sizing the canvas so that it will look good on the iPhone. One of many ways to do this is to just check if the user is on an iphone be reading the navigator.platform variable and sizing the canvas to 480×480 – which is a pretty solid way to fill the screen at both orientations. I do this on line 72.

Another way to deal with the canvas size is a bit more complex… basically you resize the canvas to the innerWidth and innerHeight properties of the window object and then when an onresize event occurs you save the current canvas, resize the canvas and draw the old image onto the newly resized canvas. This technique is put to use in mrdoob’s harmony.

Here is a snippet from harmony to give you an idea:

  SCREEN_WIDTH = window.innerWidth;
  SCREEN_HEIGHT = window.innerHeight;
 
        /* make a copy */
  savecanvas = document.createElement("canvas");
  savecanvas.width = canvas.width;
  savecanvas.height = canvas.height;
 
  savecanvas.getContext("2d").drawImage(canvas, 0, 0);
 
        /* change the size */
  canvas.width = SCREEN_WIDTH;
  canvas.height = SCREEN_HEIGHT;
 
        /* draw the copy */
  context.drawImage(savecanvas, 0, 0);

The touch events themselves are pretty easy to understand. You’ve got:

touchstart
touchend
touchmove
touchcancel

These are all pretty self explanitory and are easy enough to add:

document.addEventListener("touchstart", function(e){
   // do things
}, false);
 
// or :
document.ontouchstart = function(e){
  // do things
}

The touch events have a nice property called touches, which is an array of objects. You can get the x and y locations on the page by doing:

document.ontouchstart = function(e){
  var touch = e.touches[0];
  console.log(touch.pageX, touch.pageY);
}

There is more written about these events in the docs and on this blog post I found.

You can also read up on the window.onorientationchange event, which I use to make sure the url bar stays hidden. It can also be used to determine the orientation of the phone.

I was thinking about making an erase feature that is trigged by shaking the phone and then decided to leave it out for simplicity. I did however find a really nice snippet for doing just that. Check it out here.

Almost forgot… When I first started making sites with safari mobile in mind I was pissed that I couldn’t get rid of the url bar. After some digging I came across this trick on stackoverflow:

     window.onload = function() {
        setTimeout(function(){
           window.scrollTo(0, 1);
        }, 100);
      }

I dug around for the original link, but couldn’t seem to find it.



 
 
 

Leave a Reply

Spam protection by WP Captcha-Free