hcg-pinch-zoom - Live Demo

Interactive demos of hcg-pinch-zoom, a lightweight zero-dependency JavaScript library for pinch and wheel zoom. Try touch pinch, touchpad pinch (wheel + ctrlKey), Ctrl + mouse wheel, slider zoom with setScale(), built-in drag while zoomed, minScale / maxScale. Version . hcg-pinch-zoom - Full documentation

1. Basic

Add data-hcg-pinch-zoom to a viewport. The first child is the zoomable content.

Try it
Sample landscape for pinch zoom demo
View usage code
<div class="pz-viewport" data-hcg-pinch-zoom>
  <img src="photo.jpg" alt="Photo">
</div>

2. Touch pinch

On touch devices, use two fingers to pinch zoom. Large diagram content shows pan after zooming in.

Try it

Architecture diagram

Pinch with two fingers on a phone or tablet. Zoom centers on the midpoint between your fingers.

View usage code
<div class="pz-viewport diagram-viewport">
  <div class="pz-diagram">
    <h3>Architecture diagram</h3>
    <p>Pinch with two fingers on a phone or tablet.</p>
  </div>
</div>

hcgPinchZoom('.diagram-viewport', {
  pinch: true,
  drag: true
});

3. Wheel zoom

Hold Ctrl and scroll the mouse wheel, or use a trackpad pinch gesture. Both use the same wheel handler with ctrlKey.

Try it
Sample photo for wheel zoom demo
View usage code
<div class="pz-viewport" id="viewport-wheel">
  <img src="photo.jpg" alt="Photo">
</div>

hcgPinchZoom('#viewport-wheel', {
  wheel: true
});

4. Slider zoom

Use an HTML range input with setScale() for zoom. onZoom updates the slider when pinching or scrolling.

Try it
Sample photo for slider zoom demo
View usage code
<label for="zoom-slider">Scale: <span id="slider-readout">1.00</span></label>
<input type="range" id="zoom-slider" min="0.5" max="4" step="0.01" value="1">
<div class="pz-viewport" id="viewport-step">
  <img src="photo.jpg" alt="Photo">
</div>
<button type="button" id="btn-slider-reset">reset()</button>

const slider = document.getElementById('zoom-slider');
const readout = document.getElementById('slider-readout');
const viewport = document.getElementById('viewport-step');

const pz = hcgPinchZoom('#viewport-step', {
  minScale: 0.5,
  maxScale: 4,
  onZoom(detail) {
    slider.value = detail.scale;
    readout.textContent = detail.scale.toFixed(2);
  }
});

slider.addEventListener('input', () => {
  const v = Number(slider.value);
  const rect = viewport.getBoundingClientRect();
  pz.setScale(v, {
    clientX: rect.left + rect.width / 2,
    clientY: rect.top + rect.height / 2
  });
});

document.getElementById('btn-slider-reset').addEventListener('click', () => {
  pz.reset();
  slider.value = '1';
  readout.textContent = '1.00';
});

5. Drag while zoomed

Zoom in or out with Ctrl+wheel or pinch, then drag with one finger or mouse. Drag is built in: when scale is 100%, drag is off and position resets.

Try it
Sample photo for drag while zoomed demo
View usage code
<div class="pz-viewport" id="viewport-pan">
  <img src="photo.jpg" alt="Photo">
</div>
<button type="button" id="btn-pan-reset">reset()</button>

const pz = hcgPinchZoom('#viewport-pan', {
  drag: true,
  onPan(detail) {
    console.log(detail.x, detail.y, detail.scale);
  }
});

document.getElementById('btn-pan-reset').addEventListener('click', () => {
  pz.reset();
});

6. Min and max scale

Limit zoom range with data-hcg-min-scale and data-hcg-max-scale (here 1 to 2.5).

Try it
Sample photo with zoom limits
View usage code
<div class="pz-viewport" data-hcg-pinch-zoom data-hcg-min-scale="1" data-hcg-max-scale="2.5">
  <img src="photo.jpg" alt="Photo">
</div>

7. Reset and setScale()

Call instance methods to reset or jump to a scale level programmatically.

Try it

Scale: 1

Sample photo for API demo
View usage code
<p id="scale-readout">Scale: 1</p>
<div class="pz-viewport" id="viewport-api">
  <img src="photo.jpg" alt="Photo">
</div>
<button type="button" id="btn-zoom-in">setScale(2)</button>
<button type="button" id="btn-zoom-out">setScale(1)</button>
<button type="button" id="btn-reset">reset()</button>

const pz = hcgPinchZoom('#viewport-api', {
  onZoom(detail) {
    document.getElementById('scale-readout').textContent =
      'Scale: ' + detail.scale.toFixed(2);
  }
});

document.getElementById('btn-zoom-in').addEventListener('click', () => {
  pz.setScale(2);
});
document.getElementById('btn-zoom-out').addEventListener('click', () => {
  pz.setScale(1);
});
document.getElementById('btn-reset').addEventListener('click', () => {
  pz.reset();
});

// Or without storing the instance:
hcgPinchZoom.reset('#viewport-api');

8. Zoom below 100%

minScale: 0.5 lets you zoom out to 50%. Drag is enabled whenever scale is not 100%.

Try it
Sample photo zoomed below 100 percent
View usage code
<div class="pz-viewport" id="viewport-contain">
  <img src="photo.jpg" alt="Photo">
</div>
<button type="button" id="btn-contain-reset">reset()</button>

const pz = hcgPinchZoom('#viewport-contain', {
  minScale: 0.5
});

document.getElementById('btn-contain-reset').addEventListener('click', () => {
  pz.reset();
});

9. Clickable content

Buttons and links inside zoomable content stay clickable, including after zoom. The library skips pan on interactive elements (button, a, input, and data-hcg-pinch-zoom-interactive). Demo images use pointer-events: none; interactive children must not. Zoom in, then click a region.

Try it

Last click: none

Interactive map

Click a region below. Works at 100% zoom and after zooming in.

View usage code
<p>Last click: <span id="click-readout">none</span></p>
<div class="pz-viewport" id="viewport-clickable">
  <div class="pz-interactive">
    <h3>Interactive map</h3>
    <button type="button" class="pz-hotspot" data-region="North">North</button>
    <!-- more buttons -->
  </div>
</div>

hcgPinchZoom('#viewport-clickable', { drag: true });

document.querySelectorAll('.pz-hotspot').forEach((btn) => {
  btn.addEventListener('click', () => {
    document.getElementById('click-readout').textContent = btn.dataset.region;
  });
});

/* Do not use pointer-events: none on interactive children.
   For zoom-only (no pan), use drag: false so clicks work while zoomed. */

10. Drag disabled

Set drag: false to zoom only. Useful for image viewers where pan is not needed.

Try it
Zoom only, no drag
View usage code
<div class="pz-viewport" id="viewport-drag-off">
  <img src="photo.jpg" alt="Photo">
</div>

hcgPinchZoom('#viewport-drag-off', {
  drag: false
});