Good pinch-to-zoom in image object?

Richard Gaskin ambassador at
Sat Jan 27 14:47:50 EST 2018

Brian Milby wrote:

 > Here's what I came up with.  It is a little slow though.  It's pretty
 > much a port of the code that I linked.  I had to adapt to the way move
 > events are issued in LC (particularly that a touch start does not
 > include coordinates).  I also added the ability to continue moving
 > when a zoom stops (only one touch ended).  I initially was calling the
 > "updateImage" handler using send in time (and checking pending
 > messages to avoid calling multiple times), but the code is fast enough
 > to not benefit from that (adding delay made it less smooth, the
 > "updateImage" handler always seemed to finish before another move
 > event was queued).
 > I put the code in the graphic.  I called the "initGraphic" handler
 > from the card preOpenCard handler.

Very kind of you, Brian.  Thanks!  Much obliged.

I just gave it a whirl and it seems to work rather well.  MUCH better 
for me than embedding an entire browser app inside my app just to get a 
good pinch.

Since I was working on an image rather than a graphic, I modded the 
script to use a script-local object specifier rather than the hard-wired 
graphic reference, then added a mouseDown handler to trigger your init 
so I could test it easily.

While I was at it I re-wrapped a few things to hopefully fit better here 
for others to easily copy-n-paste.

Thanks again - it's nicely done.

@Michael Doub:  another candidate for MasterLib?

  Richard Gaskin
  Fourth World Systems


local panning, \
       zooming, \
       startX0, startY0, \
       startX1, startY1, \
       endX0, endY0, \
       endX1, endY1, \
       startDistanceBetweenFingers, \
       endDistanceBetweenFingers, \
       pinchRatio, \
       imgWidth, imgHeight, \
       currentContinuousZoom, \
       currentOffsetX, currentOffsetY, \
       currentWidth, currentHeight, \
       newContinuousZoom, \
       newHeight, newWidth, \
       newOffsetX, newOffsetY, \
       centerPointStartX, centerPointStartY, \
       centerPointEndX, centerPointEndY, \
       translateFromZoomingX, translateFromZoomingY, \
       translateFromTranslatingX, translateFromTranslatingY, \
       translateTotalX, translateTotalY, \
       percentageOfImageAtPinchPointX, \
       percentageOfImageAtPinchPointY, \
       sTouchArray, sTouch0, sTouch1

local sZoomObj

on mouseDown
    put the long id of me into sZoomObj -- Change this to specify object
end mouseDown

on initGraphic
    local tLoc
    put the width of sZoomObj into imgWidth
    put the height of sZoomObj into imgHeight
    put 1.0 into currentContinuousZoom
    put the left of sZoomObj into currentOffsetX
    put the top of sZoomObj into currentOffsetY
    put imgWidth into currentWidth
    put imgHeight into currentHeight
end initGraphic

on touchMove pID, pX, pY
    -- capture current position
    put pX into sTouchArray[pID]["x"]
    put pY into sTouchArray[pID]["y"]

    -- since the touch start doesn't include location, need to handle
    -- the calculations here
    if sTouchArray[pID]["started"] is empty then
       put true into sTouchArray[pID]["started"]
       if the number of lines of the keys of sTouchArray is 1 then
          put pID into sTouch0
          put pX into startX0
          put pY into startY0
       else if the number of lines of the keys of sTouchArray is 2 then
          put pID into sTouch1
          put sTouchArray[sTouch0]["x"] into startX0
          put sTouchArray[sTouch0]["y"] into startY0
          put pX into startX1
          put pY into startY1
          put ((startX0 + startX1) / 2.0) into centerPointStartX
          put ((startY0 + startY1) / 2.0) into centerPointStartY
          put (centerPointStartX - currentOffsetX) / currentWidth \
                into percentageOfImageAtPinchPointX
          put (centerPointStartY - currentOffsetY) / currentHeight \
                into percentageOfImageAtPinchPointY
          put sqrt((startX1-startX0)^2 + (startY1-startY0)^2) \
                into startDistanceBetweenFingers
       end if
       exit touchMove
    end if

    -- record the end touch locations for the move
    if panning then
       put pX into endX0
       put pY into endY0
    else if zooming then
       put sTouchArray[sTouch0]["x"] into endX0
       put sTouchArray[sTouch0]["y"] into endY0
       put sTouchArray[sTouch1]["x"] into endX1
       put sTouchArray[sTouch1]["y"] into endY1
    end if

end touchMove

on touchEnd pID
    put true into sTouchArray[pID]["ended"]
end touchEnd

on updateImage
    lock screen
    if panning then
       put endX0 - startX0 into translateFromTranslatingX
       put endY0 - startY0 into translateFromTranslatingY
       put currentOffsetX + translateFromTranslatingX into newOffsetX
       put currentOffsetY + translateFromTranslatingY into newOffsetY
    else if zooming then
       -- Calculate current distance between points to get new-to-old
       -- pinch ratio and calc width and height
       put sqrt((endX1-endX0)^2 + (endY1-endY0)^2) \
             into endDistanceBetweenFingers
       put endDistanceBetweenFingers/startDistanceBetweenFingers \
          into pinchRatio
       put pinchRatio * currentContinuousZoom into newContinuousZoom
       put imgWidth * newContinuousZoom into newWidth
       put imgHeight * newContinuousZoom into newHeight

       -- Get the point between the two touches, relative to upper-left
       -- corner of image
       put ((endX0 + endX1) / 2.0) into centerPointEndX
       put ((endY0 + endY1) / 2.0) into centerPointEndY

       -- This is the translation due to pinch-zooming
       put (currentWidth - newWidth) * percentageOfImageAtPinchPointX \
          into translateFromZoomingX
       put (currentHeight - newHeight) * percentageOfImageAtPinchPointY \
          into translateFromZoomingY

       -- And this is the translation due to translation of the
       -- centerpoint between the two fingers
       put centerPointEndX - centerPointStartX into \
       put centerPointEndY - centerPointStartY into \

       -- Total translation is from two components:
       --    (1) changing height and width from zooming and
       --    (2) from the two fingers translating in unity
       put translateFromZoomingX + translateFromTranslatingX \
	 into translateTotalX
       put translateFromZoomingY + translateFromTranslatingY \
	 into translateTotalY

       -- the new offset is the old/current one plus the total
       -- translation component
       put currentOffsetX + translateTotalX into newOffsetX
       put currentOffsetY + translateTotalY into newOffsetY

       -- Set the image attributes on the card
       set the width of sZoomObj to newWidth
       set the height of sZoomObj to newHeight
    end if
    set the left of sZoomObj to newOffsetX
    set the top of sZoomObj to newOffsetY

    -- clear touch array for ended touches after updating
    repeat for each key tKey in sTouchArray
       if sTouchArray[tKey]["ended"] then
          if tKey is sTouch0 then
             put sTouch1 into sTouch0
             put endX1 into endX0
             put endY1 into endY0
          end if
          if panning or zooming then
             put newOffsetX into currentOffsetX
             put newOffsetY into currentOffsetY
          end if
          if zooming then
             put newWidth into currentWidth
             put newHeight into currentHeight
             put newContinuousZoom into currentContinuousZoom
             put endX0 into startX0
             put endY0 into startY0
          end if
          delete variable sTouchArray[tKey]
       end if
    end repeat
    unlock screen
end updateImage

on updatePanZoomState
    put false into panning
    put false into zooming
    if the number of lines of the keys of sTouchArray is 1 then
       put true into panning
    else if the number of lines of the keys of sTouchArray is 2 then
       put true into zooming
    end if
end updatePanZoomState

More information about the use-livecode mailing list