One finger to pan, two fingers to zoom


Today I'm learning to implement some gestures we take for granted on mobile devices.

v ={x=0, y=0, w=Safe_width, h=Safe_height, zoom=1.0}  -- viewport
f,s = nil  -- ids of first and second touches
start, curr = {}, {}  -- coords of touches by id
initzoom = nil
initpos = nil -- for panning
function car.draw()
  color(1,0,0)
  rect('fill', vx(100), vy(100), scale(400), scale(200))
  color(0.5, 0.5, 0.5)
  for _,touch in ipairs(touches()) do
    if curr[touch] then
      circle('fill', curr[touch].x, curr[touch].y, 10)
    end
  end
end
function car.touchpressed(id, x,y, ...)
  if f == nil then
    f = id
    initpos = {x=v.x, y=v.y}
  else
    s = id
    initzoom = v.zoom
  end
  start[id] = {x=x, y=y}
  curr[id] = {x=x, y=y}
end
function car.touchreleased(id, x,y, ...)
  f,s = nil
  start, curr = {}, {}
  initzoom = nil
  initpos = nil
end
function car.touchmoved(id, x,y, ...)
  if start[id] then
    curr[id] = {x=x, y=y}
    if s then
      v.zoom = dist(curr[f], curr[s])/dist(start[f], start[s])*initzoom
    elseif f then
      v.x = initpos.x + start[f].x - x
      v.y = initpos.y + start[f].y - y
    end end end
function vx(sx)
  return scale(sx-v.x)
end
function vy(sy)
  return scale(sy-v.y)
end
function scale(d)
  return d*v.zoom
end
function dist(p1, p2)
  return ((p2.x-p1.x)^2 + (p2.y-p1.y)^2) ^ 0.5
end
function str(s) return tostring(s) end

65 lines (though itch.io keeps deleting the blank lines I paste in)

If you try pasting this program into Lua Carousel, remember to first run the abbreviations on one of the example screens. Or if you've deleted that screen, here are the abbreviations I used in this post:

g = love.graphics
rect, circle = g.rectangle, g.circle
color = g.setColor
touches = love.touch.getTouches

Get Lua Carousel

Comments

Log in with itch.io to leave a comment.

(3 edits)

Zoom to the screen middle point, to the middle of multiple touches or to the top left corner?

(1 edit) (+1)

That is a good question, if I understand you right. The above program keeps the top-left corner of the viewport fixed. Is that what you meant?

Since then I made a change to it to keep the centroid between fingers fixed:

function car.touchmoved(id, x,y, ...)
  if start[id] then
    curr[id] = {x=x, y=y}
    if s then
      local oldzoom = v.zoom
      v.zoom = dist(curr[f], curr[s])/dist(start[f], start[s])*initzoom
      adjust_viewport(oldzoom, v.zoom)
    elseif f then
      v.x = initpos.x + iscale(start[f].x - x)
      v.y = initpos.y + iscale(start[f].y - y)
    end end end
function adjust_viewport(oldzoom, zoom)
  -- ensure centroid of fingers remains in view
  local c = centroid(curr[f], curr[s])
  v.x = v.x + c.x/oldzoom - c.x/zoom
  v.y = v.y + c.y/oldzoom - c.y/zoom
end
function centroid(a, b)
  return{x=(a.x+b.x)/2, y=(a.y+b.y)/2}
end
function iscale(d) return d/v.zoom end