Skip to content

11. Handling touches – simple pinch and zoom

March 26, 2013

In this post, we’ll look at a simple pinch and zoom class, as an introduction to handling touches – and get a little more practice with classes. This one is quite short.

You should also look at this later post, which explains touch more fully.

We’ll draw an image on the screen, and zoom it in or out as the user pinches or zooms with two fingers. We’ll calculate the zoom very simply, as the ratio of the distance between the fingers now, to the distance the last time we measured it. So if the fingers are 100 apart now, and they were 80 before, we’ll apply a zoom of 100/80 = 125%.

Codea tells you about touches using this function in Main

function touched(touch)
  -- your code here

Every single touch calls this function (two fingers means it is called twice), and it keeps getting called as you hold your finger(s) down, until you let go.

Codea provides the touch item when it calls touched, and this has lots of useful information about the touch (see the built in help for full details – press the little square at top left of the main Codea screen and find your way from there).

Probably the most important information is the state property, which has three values – BEGAN, for the start of the touch, MOVING, while the touch continues, and ENDED. The BEGAN and ENDED states will occur exactly once for each touch, but the MOVING state could occur dozens of times, depending on the length of the touch.

There is also an x and y property to tell you where the touch happened. This example shows all of these in action. It will print a message when each touch ends.

function touched(touch)
  if touch.state==ENDED then 
       print('You touched',touch.x,touch.y)

These items are all we will need for pinch and zoom. The problem, though, is that the touched function is called separately for each finger touch. How will we identify a two finger touch, as opposed to a three or four finger touch?

What I’ve done is to use a table “tb” to store a couple of details about each touch, and in the draw function, we’ll count the number of touches in the table. If there are two, we’ll calculate the distance between the fingers and compare it with the previous value, which we’ve stored. We can then ratio the two distances to calculate zoom, which is then used to draw the sprite.

For neatness, I’m going to put my pinch code into a class, like so (it’s mainly comments)


function pinch:init()
    self.tb={}. -- this table will hold touch info
    self.zoom=1 --set the initial zoom

--this function doesn't call itself. Only the touched 
--function in Main does that, but we will get that 
--function to call this one, and pass the touch info across
function pinch:touched(touch) 
    --when touch ends, clear table and set distances to nil
    if touch.state==ENDED then self.tb={} self.d1=nil self.d2=nil
    --if we are touching, add touch to tb, store x and y
    else table.insert(self.tb,{x=touch.x,y=touch.y}) end

--draw in Main will call this function, to see if we 
--need to pinch/zoom
function pinch:processTouches()  
    if #self.tb==2 then -- continue if we had two touches
        --set the two vectors for the two fingers
        local v1=vec2(self.tb[1].x,self.tb[1].y),
        local v2=vec2(self.tb[2].x,self.tb[2].y)
        self.d1=v1:dist(v2). -- calculate the distance between
    --if we've got a previous measurement, check if its 
    --changed and set the zoom
    if self.d2~=nil then self.zoom=self.zoom*self.d1/self.d2 end
    --store the current distance and clear the array 
    self.d2=self.d1  self.tb={}

The code below illustrates how to use the class. It only takes about 4 lines of code. As you get to use classes more and more, you will increasingly rely on those classes to handle touches and drawing, rather than Main, so it’s important to know how to do it.

function setup()
    img=readImage('Small World:Mine Medium')
    p=pinch() --initialise pinch

function draw()
    p:processTouches(). --process touches
    sprite(img,WIDTH/2,HEIGHT/2,500*p.zoom) --note zoom

function touched(touch)
    p:touched(touch) --pass the touch data to p

And the full code is here or here.


From → animation

  1. Ken Duerden permalink

    Thanks for this lesson. I had been having trouble with touches and in a game I am developing to learn Codea I was getting double inputs whenever I touched the screen. Proper use of the touch.state ENDED has sorted this for me. Brilliant !

  2. Saurabh permalink

    What does “~=” symbol mean?
    It can’t be almost equal it, can it?

    • Saurabh, it means not equal to – but you really need to do some reading.

      Have a look at the Lua documentation (I think I gave a link to it in about post 4), and try to learn the basics of the language. You can’t learn everything from my posts – they are intended only to explain the difficult parts of the language, and you need to read up on the easy stuff by yourself.

  3. Saurabh permalink

    I didn’t understand one thing. where do you calculate self.d2 before using it in processTouches to set the zoom

  4. Saurabh permalink

    But doesn’t that mean that self.d1 is equal to self.d2 hence self.d1/self.d2 will always be 1?

    • Nope. Self.d1 is set first, then if self.d2 is not blank, the zoom is calculated (self.d1 will not be the same as self.d2), and then self.d2 is set to self.d1.

      So self.d2 always has the value of self.d1 from the previous redraw

  5. Saurabh permalink

    Oh okay understood thanks

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: