For the JavaScript and CSS to function correctly, the Widget Tree must follow this specific nesting:
Container (ImageWrapper): The outer viewport. Acts as the clipping mask.
Container (ZoomToolbar): The floating UI. Must be a "sibling" of the Target to remain fixed.
Container (ImageTarget): The actual canvas that moves (Pan/Drag) and scales (Zoom).
Image: The native OutSystems Image widget.
When implementing the JavaScript node in the OnReady client action, define these two inputs:
OnReady
ContainerId: The Runtime ID of the ImageWrapper widget.
ImageWrapper
TargetId: The Runtime ID of the ImageTarget widget.
ImageTarget
.pan-zoom-wrapper
position: relative; overflow: hidden; touch-action: none; cursor: grab;
.pan-zoom-target
will-change: transform; transition: transform 0.2s ease-out;
.zoom-tool-bar
position: absolute; z-index: 100; display: flex;
Mouse Wheel: Zoom in/out at the cursor location.
UI Buttons: Programmatic zoom centered on the current viewport.
Activation: Triggered by mousedown (left-click) or touchstart.
mousedown
touchstart
Feedback: The cursor changes to grabbing during the movement to provide visual feedback.
grabbing
Physics: Uses translate3d for sub-pixel precision and smooth motion.
translate3d
The script exposes the following functions to the window object:
window
window.zoomIn(): Increases scale by 0.2x relative to the viewport center.
window.zoomIn()
window.zoomOut(): Decreases scale by 0.2x (clamped to a minimum of 0.2x).
window.zoomOut()
window.resetZoom(): Re-runs the centerAndFit logic to restore the original view.
window.resetZoom()
centerAndFit
Copy the provided CSS into your Screen or Block Stylesheet. Ensure the ImageWrapper has a defined height (e.g., 600px or 100%).
600px
100%
Place the optimized JavaScript code in a JavaScript Node within the OnReady event. Map the ContainerId and TargetId parameters correctly.
ContainerId
TargetId
For your Zoom In/Out/Reset buttons:
Set the OnClick event to a Client Action.
Inside the action, use a JavaScript Node to call the global method (e.g., window.zoomIn();).
window.zoomIn();
Crucial: In the Attributes (Extended Properties) of each button, add:
Property: onclick
onclick
Value: "event.stopPropagation();"
"event.stopPropagation();"
(This prevents the Drag logic from being triggered accidentally when clicking buttons).
The component includes an onload listener. If your image is dynamic, the component will wait for the image to load before calculating the initial center and scale, ensuring a flicker-free start.
onload