Share My Creation Image Center Zoom with Pan

An imageview is used to host a rather large image which then could be zoomed, centering the image during the zoom, and panned.
It can be converted to a custom view. Zooming center loses its meaning when the image is fully displayed and pan is not possible, another center
for zoom could now be selected. The zoom is done by two buttons (+/-). Panning is done by two-finger touch (pinch in and out).
Watch the b4a demo on my Youtube channel
picture from: https://unsplash.com/photos/green-s...ntains-during-daytime-photography-lWAOc0UuJ-A
B4X:
#Region  Project Attributes
    #MainFormWidth: 900
    #MainFormHeight: 680
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form

    Private ZoomPane As Pane
    Private ZoomImageView As ImageView
    Private ScrollContainer As ScrollPane
    Private btnZoomIn As Button
    Private btnZoomOut As Button
    Private bmp As Image
    Private ScaleFactor As Float = 1.0
    Private TargetScaleFactor As Float
    Private AnimationSteps As Int = 10
    Private AnimationStepCounter As Int
    Private AnimationTimer As Timer

    Private CurrentXOffset As Float = 0
    Private CurrentYOffset As Float = 0

    Private LastX As Float
    Private LastY As Float
    Private StartX As Double
    Private StartY As Double
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1") ' Load layout if you have one
    MainForm.SetFormStyle("UNIFIED")
    MainForm.Show
    btnZoomIn.Initialize("btnZoomIn")
    btnZoomOut.Initialize("btnZoomOut")
    MainForm.RootPane.AddNode(btnZoomIn,0,20,50,20)
    MainForm.RootPane.AddNode(btnZoomOut,70,20,50,20)
    btnZoomIn.TextSize=24
    btnZoomIn.Text="+"
    btnZoomOut.TextSize=24
    btnZoomOut.Text="-"
   
    ' Initialize UI components
    ScrollContainer.Initialize("sc")
    MainForm.RootPane.AddNode(ScrollContainer, 0, 75, 900, 600)
   
    ZoomPane.Initialize("zp")
    ScrollContainer.InnerNode = ZoomPane

    ZoomImageView.Initialize("zimg")
    ZoomPane.AddNode(ZoomImageView, 0, 0, 600, 400)
   
    ' Load Image
    bmp = fx.LoadImage(File.DirAssets, "nature.jpg")
    ZoomImageView.SetImage(bmp)

    ' Initialize Timer
     AnimationTimer.Initialize("AnimationTimer", 20)
    ' Use jReflection to handle mouse events
    '/AddMouseEvents(ZoomPane)
    ' Center the image
    CenterImage

    ' Enable mouse events for pan
''    ZoomImageView.SetMousePressed(True)
''    ZoomPane.AddEventHandler("OnMousePressed", "Pane_MousePressed")
''    ZoomPane.AddEventHandler("OnMouseDragged", "Pane_MouseDragged")
End Sub

Sub AddMouseEvents(Pane As Pane)
    Dim r As Reflector
    r.Target = Pane
    r.AddEventHandler("MousePressed","javafx.scene.input.MouseEvent.MOUSE_PRESSED" )
    r.AddEventHandler("MouseDragged","javafx.scene.input.MouseEvent.MOUSE_DRAGGED")
End Sub

Sub MousePressed_Event (EventData As Object)
    Dim r As Reflector
    r.Target = EventData
    StartX = r.GetField("sceneX") ' Get the X position of the mouse press
    StartY = r.GetField("sceneY") ' Get the Y position of the mouse press
End Sub

Sub MouseDragged_Event (EventData As Object)
    Dim r As Reflector
    r.Target = EventData
    Dim CurrentX As Double = r.GetField("sceneX")
    Dim CurrentY As Double = r.GetField("sceneY")

    ' Pan logic
    Dim dx As Double = CurrentX - StartX
    Dim dy As Double = CurrentY - StartY
    StartX = CurrentX
    StartY = CurrentY
    ZoomPane.Left = ZoomPane.Left + dx
    ZoomPane.Top = ZoomPane.Top + dy
End Sub
Sub zp_MousePressed (Event As MouseEvent)
    LastX = Event.X
    LastY = Event.Y
End Sub

Sub zp_MouseDragged (Event As MouseEvent)
    Dim dx As Float = Event.X - LastX
    Dim dy As Float = Event.Y - LastY
   
    CurrentXOffset = CurrentXOffset + dx
    CurrentYOffset = CurrentYOffset + dy

    DrawImage
    LastX = Event.X
    LastY = Event.Y
End Sub

Sub CenterImage()
    CurrentXOffset = (ScrollContainer.Width - bmp.Width * ScaleFactor) / 2
    CurrentYOffset = (ScrollContainer.Height - bmp.Height * ScaleFactor) / 2
    DrawImage
End Sub

Sub btnZoomIn_Click
    StartSmoothZoom(ScaleFactor * 1.2)
End Sub

Sub btnZoomOut_Click
    StartSmoothZoom(ScaleFactor / 1.2)
End Sub

Sub StartSmoothZoom(NewScaleFactor As Float)
    If NewScaleFactor < 0.5 Or NewScaleFactor > 5 Then Return

    TargetScaleFactor = NewScaleFactor
    AnimationStepCounter = 0
    AnimationTimer.Enabled = True
End Sub

Sub AnimationTimer_Tick
    Dim factor As Float = AnimationStepCounter / AnimationSteps
    Dim interpolatedScale As Float = ScaleFactor + (TargetScaleFactor - ScaleFactor) * factor

    ZoomImage(interpolatedScale)
    AnimationStepCounter = AnimationStepCounter + 1

    If AnimationStepCounter > AnimationSteps Then
        ScaleFactor = TargetScaleFactor
        AnimationTimer.Enabled = False
    End If
End Sub

Sub ZoomImage(NewScaleFactor As Float)
    Dim visibleCenterX As Float = (ScrollContainer.Width / 2 - CurrentXOffset) / ScaleFactor
    Dim visibleCenterY As Float = (ScrollContainer.Height / 2 - CurrentYOffset) / ScaleFactor

    ScaleFactor = NewScaleFactor

    CurrentXOffset = ScrollContainer.Width / 2 - visibleCenterX * ScaleFactor
    CurrentYOffset = ScrollContainer.Height / 2 - visibleCenterY * ScaleFactor

    Dim newWidth As Float = bmp.Width * ScaleFactor
    Dim newHeight As Float = bmp.Height * ScaleFactor

    Dim minXOffset As Float = Min(0, ScrollContainer.Width - newWidth)
    Dim minYOffset As Float = Min(0, ScrollContainer.Height - newHeight)

    CurrentXOffset = Max(minXOffset, Min(CurrentXOffset, 0))
    CurrentYOffset = Max(minYOffset, Min(CurrentYOffset, 0))

    DrawImage
End Sub

Sub DrawImage()
    ZoomPane.SetSize(bmp.Width * ScaleFactor, bmp.Height * ScaleFactor)

    ZoomImageView.SetLayoutAnimated(0, CurrentXOffset, CurrentYOffset, bmp.Width * ScaleFactor, bmp.Height * ScaleFactor)
End Sub
 
Last edited:
Top