A library for panning and zooming elements using CSS transforms :mag:

Overview

Panzoom

Build Status

Examples


Panzoom is a small library (~3.7kb gzipped) to add panning and zooming functionality to an element. Rather than using absolute positioning or setting width and height, Panzoom uses CSS transforms to take advantage of hardware/GPU acceleration in the browser, which means the element can be anything: an image, a video, an iframe, a canvas, text, WHATEVER.

For common support questions, see the FAQ.

Browser support

Here is a list of currently supported browsers.

Mobile support

iOS, Android, and Windows Mobile are supported.

Panzoom includes support for touch gestures and even supports pinch gestures for zooming. It is perfectly suited for both mobile and desktop browsers. It uses pointer events by default wherever supported.

SVG support

Panzoom supports panning and zooming SVG elements directly.

In IE11, CSS animations/transitions do not work on SVG elements, at least for the transform style. They do work in other browsers.

One could implement transitions manually in IE11 using the setTransform option and integrating a tweening library for javascript animations (such as tween.js).

Installing

With npm:

$ npm install --save @panzoom/panzoom

With yarn:

$ yarn add @panzoom/panzoom

Panzoom uses UMD and can be loaded a lot of ways.

With ES6 imports:

import Panzoom from '@panzoom/panzoom'

With commonjs or browserify:

const Panzoom = require('@panzoom/panzoom')

With an AMD loader in an anonymous module:

define(['@panzoom/panzoom'], function (Panzoom) {
  const elem = document.getElementById('panzoom-element')
  Panzoom(elem)
})

With a script tag:

<script src="/js/panzoom.js"></script>

With a script tag from a CDN:

<script src="https://unpkg.com/@panzoom/[email protected]/dist/panzoom.min.js"></script>

Usage

const elem = document.getElementById('panzoom-element')
const panzoom = Panzoom(elem, {
  maxScale: 5
})
panzoom.pan(10, 10)
panzoom.zoom(2, { animate: true })

// Panning and pinch zooming are bound automatically (unless disablePan is true).
// There are several available methods for zooming
// that can be bound on button clicks or mousewheel.
button.addEventListener('click', panzoom.zoomIn)
elem.parentElement.addEventListener('wheel', panzoom.zoomWithWheel)

FAQ

1. What is transform-origin and why is it added to the panzoom element?

  • The transform-origin is the origin from which transforms are applied. Panzoom ensures the defaults are set to what it expects to calculate focal point zooming.
  • HTML elements default to '50% 50%'.
  • SVG elements default to '0 0'.

2. I am using Panzoom with an <object> tag and it's not working. What's wrong?

Object elements can eat up events, making it so they never reach Panzoom. To fix this, disable pointer events (pointer-events: none) on the <object> tag and call Panzoom using a wrapper.

3. My links aren't working! How do I enable an anchor within a panzoom element?

Add class options.excludeClass (default is "panzoom-exclude") to whatever element you want to be clickable. Panzoom will check for this class before handling the event. Alternatively, add a reference to the element to the exclude option, or call event.stopImmediatePropagation() in an event handler on the clickable element.

A note on the async nature of Panzoom

In some cases, setting one thing and then setting another synchronously will not work as intended.

For instance, the following usually works fine.

const panzoom = Panzoom(elem)
panzoom.zoom(2)
panzoom.pan(100, 100)

However, you might find that the things start breaking when the contain option is set.

This is due to the fact that in order for Panzoom to retrieve proper dimensions, the scale needs to be painted.

If you find that things aren't looking quite right, try the following instead...

panzoom.zoom(2)
setTimeout(() => panzoom.pan(100, 100))

4. I'm using Panzoom with SVG text elements and am seeing some weird text resizing. How do I fix this?

Add text-rendering="geometricPrecision" to your <text> elements.

<text text-rendering="geometricPrecision" x="40" y="120">Hello World</text>

5. I'm using Panzoom on a canvas element that renders a PDF. How do I avoid the PDF getting blurry when scaled?

See this stackoverflow question


Documentation

Panzoom

Panzoom(elem, options?): [PanzoomObject](#PanzoomObject)

Parameters

Name Type
elem HTMLElement | SVGElement
options? Omit<[PanzoomOptions](#PanzoomOptions), "force">

Returns

[PanzoomObject](#PanzoomObject)

Defined in

panzoom.ts:59

PanzoomOptions

Includes MiscOptions, PanOptions, and ZoomOptions

MiscOptions

These options can be passed to Panzoom(), as well as any pan or zoom function. One exception is force, which can only be passed to methods like pan() or zoom(), but not Panzoom() or setOptions() as it should not be set globally.

animate

Optional animate: boolean (Default: false)

Whether to animate transitions

Defined in

types.ts:21

canvas

Optional canvas: boolean (Default: false)

This option treats the Panzoom element's parent as a canvas. Effectively, Panzoom binds the down handler to the parent instead of the Panzoom element, so that pointer events anywhere on the "canvas" moves its children. See issue #472.

Note: setting this option to true also changes where the cursor style is applied (i.e. the parent).

Defined in

types.ts:32

duration

Optional duration: number (Default: 200)

Duration of the transition (ms)

Defined in

types.ts:34

easing

Optional easing: string (Default: "ease-in-out")

CSS Easing used for transitions

Defined in

types.ts:36

exclude

Optional exclude: Element[] (Default: [])

Add elements to this array that should be excluded from Panzoom handling. Ancestors of event targets are also checked. e.g. links and buttons that should not propagate the click event.

Defined in

types.ts:43

excludeClass

Optional excludeClass: string (Default: "panzoom-exclude")

Add this class to any element within the Panzoom element that you want to exclude from Panzoom handling. That element's children will also be excluded. e.g. links and buttons that should not propagate the click event.

Defined in

types.ts:50

force

Optional force: boolean

force should be used sparingly to temporarily override and ignore options such as disablePan, disableZoom, and panOnlyWhenZoomed. This option cannot be passed to the Panzoom constructor or setOptions (to avoid setting this option globally).

// Overrides disablePan and panOnlyWhenZoomed
panzoom.pan(50, 100, { force: true })
// Overrides disableZoom
panzoom.zoom(1, { force: true })

Defined in

types.ts:66

handleStartEvent

Optional handleStartEvent: (event: Event) => void (Default: (e: Event) => { e.preventDefault() e.stopPropagation() })

On the first pointer event, when panning starts, the default Panzoom behavior is to call event.preventDefault() and event.stopPropagation() on that event. The former is almost certainly a necessity; the latter enables Panzoom elements within Panzoom elements.

But there are some cases where the default is not the desired behavior. Set this option to override that behavior.

// Only call preventDefault()
Panzoom(elem, {
  handleStartEvent: (event) => {
    event.preventDefault()
  }
})
// Do nothing.
// This can change dragging behavior on mobile.
Panzoom(elem, {
  handleStartEvent: () => {}
})
Parameters
Name Type
event Event
Returns

void

Defined in

types.ts:91

noBind

Optional noBind: boolean

Skip binding the default Panzoom event listeners

Defined in

types.ts:95

origin

Optional origin: string

Change this at your own risk. The transform-origin is the origin from which transforms are applied. Default: '50% 50%' for HTML and '0 0' for SVG. The defaults are set because changing the transform-origin on SVG elements doesn't work in IE.

Changing this should work with many things, but it will break focal point zooming, which assumes the defaults are set to do the more complicated calculations.

And again, changing this for SVG in IE doesn't work at all.

Defined in

types.ts:109

overflow

Optional overflow: string (Default: "hidden")

The overflow CSS value for the parent. Defaults to 'hidden'

Defined in

types.ts:111

pinchAndPan

Optional pinchAndPan: boolean (Default: false)

Set to true to enable panning during pinch zoom. Note: this is zooming to a point and panning in the same frame. In other words, the zoom has not yet painted and therefore the pan is working with old dimensions. Essentially, it may be best to avoid using this option when using contain.

Related issues: #512 #606

Defined in

types.ts:124

setTransform

Optional setTransform: (elem: HTMLElement | SVGElement, __namedParameters: CurrentValues, _options?: PanzoomOptions) => void

Set the transform using the proper prefix

Override the transform setter. This is exposed mostly so the user could set other parts of a transform aside from scale and translate. Default is defined in src/css.ts.

// This example always sets a rotation
// when setting the scale and translation
const panzoom = Panzoom(elem, {
  setTransform: (elem, { scale, x, y }) => {
    panzoom.setStyle('transform', `rotate(0.5turn) scale(${scale}) translate(${x}px, ${y}px)`)
  }
})
Parameters
Name Type
elem HTMLElement | SVGElement
__namedParameters CurrentValues
_options? PanzoomOptions
Returns

void

Defined in

types.ts:128

silent

Optional silent: boolean

Silence all events

Defined in

types.ts:130

startScale

Optional startScale: number (Default: 1)

Scale used to set the beginning transform

Defined in

types.ts:136

startX

Optional startX: number (Default: 0)

X Value used to set the beginning transform

Defined in

types.ts:132

startY

Optional startY: number (Default: 0)

Y Value used to set the beginning transform

Defined in

types.ts:134

touchAction

Optional touchAction: string (Default: "none")

This value is used to set touch-action on both the Panzoom element and its parent. It is needed because that the native scroll on mobile interferes with panning and pinch zooming. Set this to empty string to re-enable scrolling on mobile, but note that both scrolling and panning cannot work at the same time.

Defined in

types.ts:146

PanOptions (includes MiscOptions)

contain

Optional contain: "inside" | "outside"

Contain the panzoom element either inside or outside the parent. Inside: The panzoom element is smaller than its parent and cannot be panned to the outside. Outside: The panzoom element is larger than its parent and cannot be panned to the inside. In other words, no empty space around the element will be shown.

Note: the containment pan adjustment is not affected by the disablePan option.

Defined in

types.ts:165

cursor

Optional cursor: string (Default: "move")

The cursor style to set on the panzoom element

Defined in

types.ts:167

disablePan

Optional disablePan: boolean (Default: false)

Disable panning functionality. Note: disablePan does not affect focal point zooming or the contain option. The element will still pan accordingly.

Defined in

types.ts:173

disableXAxis

Optional disableXAxis: boolean (Default: false)

Pan only on the Y axis

Defined in

types.ts:175

disableYAxis

Optional disableYAxis: boolean (Default: false)

Pan only on the X axis

Defined in

types.ts:177

panOnlyWhenZoomed

Optional panOnlyWhenZoomed: boolean (Default: false)

Disable panning while the scale is equal to the starting value

Defined in

types.ts:181

relative

Optional relative: boolean (Default: false)

When passing x and y values to .pan(), treat the values as relative to their current values

Defined in

types.ts:179

roundPixels

Optional roundPixels: boolean

Round x and y values to whole numbers. This can help prevent images and text from looking blurry, but the higher the scale, the more it becomes necessary to use fractional pixels. Use your own judgment on how much to limit zooming in when using this option.

Defined in

types.ts:190

ZoomOptions (includes MiscOptions)

disableZoom

Optional disableZoom: boolean (Default: false)

Disable zooming functionality

Defined in

types.ts:195

focal

Optional focal: Object

Zoom to the given point on the panzoom element. This point is expected to be relative to the panzoom element's dimensions and is unrelated to the parent dimensions.

Type declaration

Name Type
x number
y number

Defined in

types.ts:202

maxScale

Optional maxScale: number (Default: 4)

The maximum scale when zooming

Defined in

types.ts:206

minScale

Optional minScale: number (Default: 0.125)

The minimum scale when zooming

Defined in

types.ts:204

step

Optional step: number (Default: 0.3)

The step affects zoom calculation when zooming with a mouse wheel, when pinch zooming, or when using zoomIn/zoomOut

Defined in

types.ts:208

PanzoomObject

These methods are available after initializing Panzoom.

bind

bind: () => void

Bind the default down, move, and up event listeners to the Panzoom element. This does not normally need to be called. It gets called by default when creating a new Panzoom object, but can be skipped with the noBind option.

const panzoom = Panzoom(elem, { noBind: true })
// ...
panzoom.bind()
Returns

void

Defined in

types.ts:235

destroy

destroy: () => void

Remove all event listeners bound to the the Panzoom element

Returns

void

Defined in

types.ts:237

eventNames

eventNames: Object

This object exposes the event names used by Panzoom, depending on the current browser's support for Pointer or Touch events.

Type declaration

Name Type
down string
move string
up string

Defined in

types.ts:243

getOptions

getOptions: () => PanzoomOptions

Returns a copy of the current options object

Returns

PanzoomOptions

Defined in

types.ts:249

getPan

getPan: () => { x: number ; y: number }

Get the current x/y translation

Returns

Object

Name Type
x number
y number

Defined in

types.ts:245

getScale

getScale: () => number

Get the current scale

Returns

number

Defined in

types.ts:247

handleDown

handleDown: (event: PointerEvent) => void

handleDown, handleMove, and handleUp are the exact event handlers that Panzoom binds to pointer events. They are exposed in case you prefer to bind your own events or extend them. Note that move and up are bound to the document, not the Panzoom element. Only the down event is bound to the Panzoom element. To avoid double-binding, also set noBind to true.

const panzoom = Panzoom(elem, { noBind: true })
elem.addEventListener('pointerdown', (event) => {
  console.log(event)
  panzoom.handleDown(event)
})
document.addEventListener('pointermove', panzoom.handleMove)
document.addEventListener('pointerup', panzoom.handleUp)
Parameters
Name Type
event PointerEvent
Returns

void

Defined in

types.ts:271

handleMove

handleMove: (event: PointerEvent) => void

Parameters
Name Type
event PointerEvent
Returns

void

Defined in

types.ts:272

handleUp

handleUp: (event: PointerEvent) => void

Parameters
Name Type
event PointerEvent
Returns

void

Defined in

types.ts:273

pan

pan: (x: string | number, y: string | number, panOptions?: PanOptions) => [CurrentValues](#CurrentValues)

Pan the Panzoom element to the given x and y coordinates

// Translates the element to 50px, 100px
panzoom.pan(50, 100)
// Pans the element right 10px and down 10px from its current position
panzoom.pan(10, 10, { relative: true })
Parameters
Name Type
x string | number
y string | number
panOptions? PanOptions
Returns

[CurrentValues](#CurrentValues)

Defined in

types.ts:284

reset

reset: (resetOptions?: PanzoomOptions) => [CurrentValues](#CurrentValues)

Reset the pan and zoom to startX, startY, and startScale. Animates by default, ignoring the global option. Pass { animate: false } to override. Reset ignores the disablePan, disableZoom, and panOnlyWhenZoomed options. Pass { force: false } to override.

panzoom.reset()
panzoom.reset({ animate: false })
Parameters
Name Type
resetOptions? PanzoomOptions
Returns

[CurrentValues](#CurrentValues)

Defined in

types.ts:297

resetStyle

resetStyle: () => void

Reset the styles set on the Panzoom element and its parent (such as overflow, cursor, etc.)

panzoom.resetStyle()
Returns

void

Defined in

types.ts:306

setOptions

setOptions: (options?: PanzoomOptions) => void

Change any number of options on a Panzoom instance. Setting some options will have side-effects. For instance, changing the cursor option will also set the cursor style.

const panzoom = Panzoom(elem, { cursor: 'move' })
// ...
panzoom.setOptions({ cursor: 'default' })
Parameters
Name Type
options? PanzoomOptions
Returns

void

Defined in

types.ts:319

setStyle

setStyle: (name: string, value: string) => void

A convenience method for setting prefixed styles on the Panzoom element

Parameters
Name Type
name string
value string
Returns

void

Defined in

types.ts:321

zoom

zoom: (scale: number, zoomOptions?: ZoomOptions) => [CurrentValues](#CurrentValues)

Zoom the Panzoom element to the given scale

panzoom.zoom(2.2)
panzoom.zoom(2.2, { animate: true })
Parameters
Name Type
scale number
zoomOptions? ZoomOptions
Returns

[CurrentValues](#CurrentValues)

Defined in

types.ts:330

zoomIn

zoomIn: (zoomOptions?: ZoomOptions) => [CurrentValues](#CurrentValues)

Zoom in using the predetermined increment set in options. Animates by default, ignoring the global option. Pass { animate: false } to override.

panzoom.zoomIn()
panzoom.zoomIn({ animate: false })
Parameters
Name Type
zoomOptions? ZoomOptions
Returns

[CurrentValues](#CurrentValues)

Defined in

types.ts:341

zoomOut

zoomOut: (zoomOptions?: ZoomOptions) => [CurrentValues](#CurrentValues)

Zoom out using the predetermined increment set in options. Animates by default, ignoring the global option. Pass { animate: false } to override.

panzoom.zoomOut()
panzoom.zoomOut({ animate: false })
Parameters
Name Type
zoomOptions? ZoomOptions
Returns

[CurrentValues](#CurrentValues)

Defined in

types.ts:352

zoomToPoint

zoomToPoint: (scale: number, point: { clientX: number ; clientY: number }, zoomOptions?: ZoomOptions) => [CurrentValues](#CurrentValues)

Zoom the Panzoom element to a focal point using the given pointer/touch/mouse event or constructed point. The clientX/clientY values should be calculated the same way as a pointermove event on the Panzoom element's parent.

panzoom.zoomToPoint(1.2, pointerEvent)
Parameters
Name Type
scale number
point Object
point.clientX number
point.clientY number
zoomOptions? ZoomOptions
Returns

[CurrentValues](#CurrentValues)

Defined in

types.ts:363

zoomWithWheel

zoomWithWheel: (event: WheelEvent, zoomOptions?: ZoomOptions) => [CurrentValues](#CurrentValues)

Zoom the Panzoom element to a focal point using the given WheelEvent

This is a convenience function that may not handle all use cases. Other cases should handroll solutions using the zoomToPoint method or the zoom method's focal option.

Notes:

  • the focal point zooming pan adjustment is not affected by the disablePan option.
  • animate should not be used when zooming with the wheel, and is therefore always disabled.
// Bind to mousewheel
elem.parentElement.addEventListener('wheel', panzoom.zoomWithWheel)
// Bind to shift+mousewheel
elem.parentElement.addEventListener('wheel', function (event) {
  if (!event.shiftKey) return
  // Panzoom will automatically use `deltaX` here instead
  // of `deltaY`. On a mac, the shift modifier usually
  // translates to horizontal scrolling, but Panzoom assumes
  // the desired behavior is zooming.
  panzoom.zoomWithWheel(event)
})
Parameters
Name Type
event WheelEvent
zoomOptions? ZoomOptions
Returns

[CurrentValues](#CurrentValues)

Defined in

types.ts:396

CurrentValues

isSVG

Optional isSVG: boolean

Defined in

types.ts:219

scale

scale: number

Defined in

types.ts:218

x

x: number

Defined in

types.ts:216

y

y: number

Defined in

types.ts:217

Events

The following events are available as custom events on the panzoom element using the native CustomEvent API. Add listeners the same way you would any other event.

elem.addEventListener('panzoomchange', (event) => {
  console.log(event.detail) // => { x: 0, y: 0, scale: 1 }
})

Notes about all events

  • The event object passed as an argument to the listener will always have a detail object with the following properties:
    • The current x value
    • The current y value
    • The current scale
    • An originalEvent property with the original event that triggered the panzoom event, if applicable. For example, the originalEvent property for a panzoomstart event would be either a pointerdown, touchstart, or mousedown event.
  • Events can be silenced when the silent option is set to true, either globally or when passed to pan, any zoom method, or reset.
  • Avoid putting too much logic in these event handlers as it could effect the performance of panning or zooming.

"panzoomstart"

Fired when the user starts a move or pinch zoom gesture on mobile.

"panzoomchange"

Fired whenever there is a pan, zoom, or reset. Note that direct calls to options.setTransform do not fire this event.

"panzoomzoom"

Fired whenever the zoom is changed by any Panzoom zoom method, directly or internally.

"panzoompan"

Fired whenever the pan is changed by the pan method, directly or internally.

"panzoomend"

Fired when the user finishes a move or finishes a pinch zoom gesture on mobile.

"panzoomreset"

Fired whenever reset is called.

Comments
  • Add Pointer Event gesture support for pinch zooming

    Add Pointer Event gesture support for pinch zooming

    Hello,

    Currently I am unable to zoom using pinch gestures on a Surface RT tablet with IE11, neither in metro interface, nor in desktop. Everything seems to be working well on Android and iOS, so I guess it has something to do with pointer events, which seem to be supported in the current version.

    I have disabled the default touch interaction for the container which is used for zooming, using the -ms-touch-action: none; css style so that such events are handled in javascript, but to no avail.

    Any idea what I might be doing wrong?

    Thanks!

    opened by tommyxd 22
  • Pinch zoom moves image away from screen center

    Pinch zoom moves image away from screen center

    Describe the bug

    For some reason, when using my Panzoom app on mobile (any device), when trying to pinch zoom the image moves to the corner right when zooming in, and to the top left when zooming out, instead of zooming in place. Sometimes, when just starting the app, zoom out works properly before moving the image, and then the problem come back.

    Zooming in and out with the scroll wheel works as it should, it's only pinch zoom that doesn't.

    Your environment

    • Latest version (installed today)
    • Vivaldi browser, Kiwi browser, Chrome, Firefox, all latest versions.

    Expected behavior

    Zoomed image should stay centered where the user is zooming in/out.

    Actual behavior

    Zoomed image moves, and zooming in centers the bottom right of the image on the screen, while zooming out centers the top left.

    opened by jsblanco 21
  • Pinch zoom broken in Chrome v55.02883.91 on Mobile

    Pinch zoom broken in Chrome v55.02883.91 on Mobile

    Subject of the issue

    Pinch zoom broken in Chrome v55.02883.91 on Mobile and Android System WebView v55.02883.91 This applies to android mobile devices using Chrome (browser) and Cordova apps using Android System WebView.

    Your environment

    • jquery.panzoom.min.js 3.2.2
    • jquery-1.11.1.min.js (for my cordova apps) however the demo web site also does not work.
    • Chrome broken (see version above). Still works using "Internet" as browser on Samsung phones.

    Steps to reproduce

    1. Get hold of a samsung galaxy S6 Edge + (or equivalent)
    2. Upgrade Chrome and/or Android System WebView to v55 etc.
    3. Load in the demo page
    4. try and zoom in on the tiger - she broken mate!

    Expected behaviour

    On previous version of Chrome (right up until v54, worked perfectly, i.e. zoomed in!

    Actual behaviour

    What happens on v55 is all you get is the pan, not the zoom and the panned image sometimes 'jumps' between pinch fingers and creates a flashing effect as the image is flipped between your two fingers.

    opened by nsshunt 20
  • After Zooming Parent div the child div is not dragging properly

    After Zooming Parent div the child div is not dragging properly

    Describe the bug

    When zooming-in/out the Parent div (using Panzoom) the child div is not dragging properly (using jQuery Draggable). Basically when I click on child div to drag, the div move away from mouse pointer. Without zooming-in/out, child div is dragging fine.

    Code HTML:

    <div id="MainDiv" class="panzoom panzoom-parent" style="position:relative">
            <img id="img01" class="" src="MyMap.jpg" width="100%" height="auto" />
    <div id="hotspot_2219" class="panzoom-exclude ui-widget-content hotspot Medium" style="top:9.836%; left:44.390%;position:absolute;"">
    <img src="someImg.jpg" width="100%" height="100%">
    </div>
    </div>"
    

    Code JS:

    ////For Parent Div
    var elem = document.getElementById('MainDiv');
    const panzoom = Panzoom(elem);
    $(".zoom-in").click(function () { panzoom.zoomIn() });
    $(".zoom-out").click(function () { panzoom.zoomOut() });
    $(".reset").click(function () { panzoom.reset() });
    
    ////For Child div dragging
    hotspot.draggable
                    ({
                        containment: '.MainDiv',
                        stack: '.hotspot',
                        cursor: 'move',
                        revert: false,
                        stop: function (event, ui) {
                            //Some Action
                            
                        },
                        start: function (event, ui) {
                            //Some Action
                        },
                    });
    

    Your environment

    • Version of panzoom 4.0.0
    • Chrome Version 84.0.4147.89 (Official Build) (64-bit)

    Expected behavior

    Actual behavior

    Steps to reproduce

    opened by mushiahmed 19
  • Double tap to zoom

    Double tap to zoom

    I presume it doesn't currently have support for double tapping to zoom (the default action for images on iOS), to implement this would I have to setup a listener for it and trigger the zooming manually?

    opened by alexcroox 19
  • contain: outside is not guaranteed on zoom

    contain: outside is not guaranteed on zoom

    Subject of the issue

    When you zoom programatically for an element that's set to contain: outside, it is not quaranteed that it covers it's parent completely after that.

    Your environment

    • Version of panzoom: current (built from master on September 14th 2019)
    • Chrome 78 (other browsers are affected as well)

    Expected behaviour

    After any zoom-operation the element should still cover it's parent completely

    Actual behaviour

    The element doesn't update it's offset so it might not cover it's complete parent

    Steps to reproduce

    See this fiddle: https://jsfiddle.net/a8jh79rp/5/

    After you click on the button the image will be offset. The button does the following:

    1. scale the image
    2. move it
    3. scale it back

    Sometimes it will also be offset after you zoom with the mousewheel

    bug docs released 
    opened by partiellkorrekt 18
  • Transitions on svg

    Transitions on svg

    I cannot get transitions to work on svg. http://jsfiddle.net/Risingson/LVmqF/5/

    Also please try this fiddle in chrome first and then in firefox. in firefox after loading the above url click on "zoom in" and see the svg vanish to a wrong matrix.

    opened by Risingson 17
  • Pinch Zoom Not Working

    Pinch Zoom Not Working

    First Let Me Thanks Timmy Willison

    Hi Timmy, First of all on behalf of all the users of your plugin let me please thank you for putting all your efforts into making (And Continue Updating) this productive plugin.

    Subject of the issue

    Issues About Pinch Zoom On Touch Enabled Devices (Android, Win10, iOS). Please Try To Give Some Time And Give Us v3.2.3

    Your environment

    jquery.panzoom v3.2.2 jquery v3.2.1 Chrome v59

    opened by PrinceDhankhar 16
  • Pinch zoom not working on Android Chrome 55

    Pinch zoom not working on Android Chrome 55

    Subject of the issue

    Pinch zoom not working on Android Chrome 55.

    Update - demo and test pages also fail: Pinch Zoom does not work on an Android device with Chrome 55 on any of the demo items here: https://timmywil.github.io/jquery.panzoom/demo/ https://timmywil.github.io/jquery.panzoom/test/ also fails.

    Both the above work fine on the same device with Chrome updates uninstalled.

    Original issue report: Like #308 I've been using jquery.panzoom for some time, and it works flawlessly. Chrome 55 has broken it on Android. In my case Pan is fine, but Pinch Zoom causes panning and flashing and no zooming. If I uninstall updates to Chrome, problem goes away. Problem is not evident on IOS Safari or older Android Chrome.

    Your environment

    • version of jquery.panzoom 2.0.5
    • version of jquery 2.1.1
    • Chrome 55 on Android Lollipop.

    Update - jquery.panzoom 3.2.2 has the same issue, plus other containment problems. Pinch Zoom only problematic on Android Chrome 55.

    opened by nich008 14
  • Using one element to pan/zoom another?

    Using one element to pan/zoom another?

    I have a strange use case where I have a custom map. I can successfully use panzoom to pan the map but I have a background rectangle element that I want users to also interact with. Users should be able to drag the background - but it won't pan the background element. Instead I want to use a callback to pan the map group instead, if this makes sense. Is there anyway in your plugin to do this? Thanks

    question 
    opened by stodge 14
  • Zoom Power Weakens at Larger Zooms

    Zoom Power Weakens at Larger Zooms

    First, let me thank you for a fantastic library. It is the only pan/zoom library I have found with great mobile and desktop support.

    It feels as if the zoom slows down at larger magnifications. I have only looked briefly at the code, but it seems like the scale increment might need to grow at larger scales to produce the same movement.

    One place that it is particularly evident is when doing a pinch zoom. When zoomed out the pinch zoom feels very 'attached' to a user's fingers. At larger zooms the image slips beneath the user's fingers.

    I have an example here, it requires a login (I will be changing that shortly).

    http://rtifact.net/#/rtifact/52b09555af4f930aa7982496

    I don't see the same issue in the demos, but I believe its simply because the max zoom isn't very high.

    opened by FreakTheMighty 13
  • Pointer-events on Safari

    Pointer-events on Safari

    Describe the bug

    I was fiddling around with the panzoom demo at: https://timmywil.com/panzoom/demo/ and noticed that using 3-fingers within panzoom element causes quite a lot of layout-shift.

    Your environment

    • Version of panzoom: Demo version.
    • Browser and browser version: iOS 16.0.3. iOS on Safari

    Expected behavior

    I'm not sure what should happen while panning with three fingers but iOS opens undo menu. Eg: https://developer.apple.com/forums/thread/714425

    Steps to reproduce

    1. Open: https://timmywil.com/panzoom/demo/
    2. Choose any of the demos.
    3. Touch screen with 3 fingers
    4. Demo tiger will flicker and might disappear from the view
    bug 
    opened by VillePakarinenPosti 2
  • feat: support attached shadow dom elements

    feat: support attached shadow dom elements

    PR Checklist

    Please review the guidelines for contributing to this repository.

    • [X] I am requesting to pull a topic/feature/bugfix branch (right side). In other words, not main.
    • [X] I have run yarn test against my changes and tests pass.
    • [X] I have added tests to prove my fix is effective or my feature works. This can be done in the form of unit tests in test/unit/ or a new or altered demo in demo/.
    • [X] I have added or edited necessary types and generated documentation (yarn docs), or no docs changes are needed.

    Description

    Updates the isAttached helper to handle shadow dom elements. ~~If the passed element's root node is a ShadowRoot, it sets parent to the ShadowRoot's host and subsequently checks if the host is attached; otherwise it falls back to the pre-existing functionality.~~

    With additional testing, I found that the prior change was not sufficient to handle multiple nested shadow dom elements. The logic has been updated to manually traverse through the dom (handling both regular & shadow nodes) to ensure that any nesting is properly accounted for.

    Fixes: #536

    opened by breakthestatic 3
  • Focal zoom with flex-centered images

    Focal zoom with flex-centered images

    Describe the bug

    Hello, I'm trying to add panzoom to an image gallery popup, but am running into issues with the focal zoom when using a centered image. I've read through the previous issues on this topic, but still can't quite understand why the focal zoom isn't working as expected when using a centered image.

    Expected behavior

    Here's an implementation of what I would like with focal zoom using plain JS: https://codepen.io/erinxi/pen/WNXzovm

    Actual behavior

    Here's a minimal reproduction of the gallery popup with panzoom: https://codepen.io/erinxi/pen/GROxNVa Zooming in to the tiger's eye does not focus on the eye like it would in the demo https://timmywil.com/panzoom/demo/.

    If I understand correctly, calculations are done in the parent element's coordinates space (with 0, 0 in the top left)? And in the panzoom demo this is accommodated for using margins, however when using flexbox there are no margins? Any insight or suggestions on the appropriate workaround on this would be greatly appreciated!

    opened by enxi2 2
  • Feature: New contain option for both inside/outside

    Feature: New contain option for both inside/outside

    What problem does this feature solve? At the moment, when choosing the contain:inside option, it is not possible to zoom the element outside the parent element. Conversely, the contain:outside option doesn't allow to zoom the element to become smaller than the parent element in both dimensions.

    Describe the solution you'd like The new option would allow the element to be contained, with the current functionality of the contain:outside when either of the dimensions of the element is larger than the parent element, and with the functionality of contain:inside otherwise. This would provide a way for the element to never be able to be zoomed or panned outside of the parent element.

    This new option could probably be called true or contained. I'd steer away from both, as that would be invalidated if any future contain option is added (see #476).

    Describe alternatives you've considered None, really. At the moment the inside/outside options are too limiting and mutually exclusive, and not setting a contain option allows the user to pan/zoom the element outside of the parent element, which is not really desirable.

    Additional context The patch to the code would look like this (note this was a patch vs the dist/panzoom.js file and not the src/panzoom.js):

    @@ -484,6 +484,7 @@
             }
             function constrainXY(toX, toY, toScale, panOptions) {
                 var opts = __assign(__assign({}, options), panOptions);
    +            var contain = opts.contain;
                 var result = { x: x, y: y, opts: opts };
                 if (!opts.force && (opts.disablePan || (opts.panOnlyWhenZoomed && scale === opts.startScale))) {
                     return result;
    @@ -496,7 +497,7 @@
                 if (!opts.disableYAxis) {
                     result.y = (opts.relative ? y : 0) + toY;
                 }
    -            if (opts.contain) {
    +            if (contain) {
                     var dims = getDimensions(elem);
                     var realWidth = dims.elem.width / scale;
                     var realHeight = dims.elem.height / scale;
    @@ -504,7 +505,10 @@
                     var scaledHeight = realHeight * toScale;
                     var diffHorizontal = (scaledWidth - realWidth) / 2;
                     var diffVertical = (scaledHeight - realHeight) / 2;
    -                if (opts.contain === 'inside') {
    +                if (contain === 'true') {
    +                    contain = ((scaledWidth > dims.parent.width) || (scaledHeight > dims.parent.height)) ? 'outside' : 'inside';
    +                }
    +                if (contain === 'inside') {
                         var minX = (-dims.elem.margin.left - dims.parent.padding.left + diffHorizontal) / toScale;
                         var maxX = (dims.parent.width -
                             scaledWidth -
    @@ -526,7 +530,7 @@
                             toScale;
                         result.y = Math.max(Math.min(result.y, maxY), minY);
                     }
    -                else if (opts.contain === 'outside') {
    +                else if (contain === 'outside') {
                         var minX = (-(scaledWidth - dims.parent.width) -
                             dims.parent.padding.left -
                             dims.parent.border.left -
    
    
    feature 
    opened by jcnventura 6
  • zoomWithWheel does not account for the delta value

    zoomWithWheel does not account for the delta value

    Describe the bug

    First of all, great library.

    One slight issue with zoomWithWheel is this line:

        const wheel = delta < 0 ? 1 : -1
    

    While we were implementing this library, I (using windows) got into a bit of a disagreement with our designer (using a mac) about the zoom speed. He was saying it was way too fast, while I was saying it felt just right.

    That's when I discovered polling rates and what exactly deltaY means. In short: mac trackpads have a much higher polling rate, and therefore a lower delta value, and vice versa for my mouse wheel. If you want a consistent experience with zoomWithWheel, I advise taking the delta into consideration, as different mice/trackpads will have different polling rates and delta values.

    We ended up doing the calculation ourselves, but if you're curious what the code is:

        const delta =
          event.deltaY === 0 && event.deltaX ? event.deltaX : event.deltaY;
        const scale = panzoom.getScale();
        const toScale = scale * Math.exp((delta * step * -1) / 300);
        const result = panzoom.zoom(toScale);
    

    This resulted in a consistent experience and the designer and myself both agree that the zooming now feels right.

    Your environment

    • Version of panzoom
      • 4.4.2
    • Browser and browser version
      • Edge Version 94.0.992.50 (Official build) (64-bit)
    opened by Foshkey 2
  • Browser normalization for scroll zoom

    Browser normalization for scroll zoom

    Your plugin works great. However, the step property does not normalize to the variances between browsers. Chrome and Firefox step large while others step small. See stackoverflow issue and github issue

    bug 
    opened by tlarsonlgdor 5
Releases(4.5.1)
  • 4.5.1(Sep 6, 2022)

  • 4.5.0(May 27, 2022)

  • 4.4.4(Jan 31, 2022)

  • 4.4.3(Oct 29, 2021)

  • 4.4.2(Oct 20, 2021)

  • 4.4.1(Jun 11, 2021)

  • 4.4.0(May 5, 2021)

  • 4.3.2(Sep 9, 2020)

  • 4.3.1(Jul 21, 2020)

  • 4.3.0(Jul 20, 2020)

  • 4.2.0(Jul 7, 2020)

  • 4.1.0(Apr 6, 2020)

  • 4.0.4(Mar 27, 2020)

  • 4.0.3(Jan 28, 2020)

  • 4.0.2(Jan 16, 2020)

  • 4.0.1(Jan 13, 2020)

  • 4.0.0(Dec 16, 2019)

    4.0.0 (2019-12-16)

    Bug Fixes

    • contain: always set scale before using constrainXY (761a0ec), closes #426
    • css: fix border width retrieval in Firefox (5d2f580)
    • events: fallback to touch and mouse events (#399) (2c4c303)
    • events: fix triggering panzoomend for one pointer event (f23e0fa), closes #428
    • handledown: exclude descendents of excluded parents (b2f943a), closes #431
    • handleup: remove pointer regardless of isPanning state (8938b29), closes #402 #403
    • reset: use setTransform passed to reset options (2adbb4e)
    • setoptions: set cursor style with the option (9c8efb4)
    • setstyle: remove unnecessary param from exposed setStyle (c9bcf94)
    • zoom: account for smaller elements and padding/border (3fe89a1)
    • zoom: need the before and after dimensions to constrain (7c2c982), closes #426
    • zoom: set min and max scale based on containment (d05f1e7), closes #426

    Features

    • basic panning and zooming functionality (e80270f)
    • clean slate with typescript, rollup, and semantic-release (27a0887)
    • centering: switch to default transform origins (b483cda)
    • contain: add contain: 'outside' option (1571e99)
    • events: add custom events for panning and zooming (#398) (7713025)
    • exclude: add exclude option; change clickableClass to excludeClass (da72c32), closes #411
    • handlestartevent: add option to handle the start event (931743a), closes #414
    • overflow: add an option to override the parent's overflow (77032bb), closes #427
    • add a destroy method (#404) (c88ef75)
    • add animate option to transition the transforms (d9a8e67)
    • pan: add contain: 'inside' option (a7078e8)
    • pan: add panOnlyWhenZoomed option (5559967)
    • panzoom: add the force option (0ba521a), closes #413
    • zoom: implement focal point zooming without matrices (5d077f1)
    • zoom: pinch zooming with pointer events! (5ddbd30)

    Performance Improvements

    • pan: make move/cancel listeners passive (f647163)

    BREAKING CHANGES

    • This is a complete rewrite of the panzoom library to be a standard JS lib that doesn't rely on jQuery, but can still integrate as a plugin
    Source code(tar.gz)
    Source code(zip)
  • 3.2.3(May 8, 2019)

    This is a small update to fix an error when accessing jQuery.cssProps, which no longer exists.

    Note on next release

    There are some known issues that have been in Panzoom for a while, but the rewrite is still in progress. I'll address as many issues as I can, but with everything changing, some old issues will become irrelevant and some new issues will be introduced. I will reopen GitHub issues when Panzoom 4.0 is ready.

    Source code(tar.gz)
    Source code(zip)
  • v3.2.2(Aug 28, 2016)

    • Fixed an issue where a pinch zoom was not recognized unless both fingers touched at very nearly the same time. This should make it much more flexible. You can add a finger at any point during the gesture and both touches will be recognized.

    See https://github.com/timmywil/jquery.panzoom/issues/270 for more info.

    Source code(tar.gz)
    Source code(zip)
  • v3.2.1(Aug 13, 2016)

    • In 3.1.0 (see 20aa64a2460d28d8941e189335baedfb2f134747), I greatly simplified the contain logic. The complexity had piled up to the point where I didn't even understand it all. Unfortunately, I made a significant error and broke all panzoom elements that weren't display: inline with text-align: center–like the ones in the demo page. This patch adds back the logic necessary to contain elements that are not already centered. Note: Panzoom does not support text-align: right. Just set it to left or center.
    • I had also removed support for pointer events, but I broke panzoom for devices that have both a mouse and touch support (for instance, some Windows laptops). While Pointer Events can be problematic to support in IE10, IE11, and Edge, I was able to work around all of the problems that have been reported so far. Pointer Event support is now better than ever!
    Source code(tar.gz)
    Source code(zip)
  • v3.2.0(Jul 26, 2016)

    IE9-11 and Edge 13-14+ do not respect transform values set through CSS. This makes it impossible to use CSS transitions on SVG elements in those browsers.

    Nevertheless, why not enable them in the browsers where it works? Chrome, Firefox, and Safari do this just fine.

    Source code(tar.gz)
    Source code(zip)
  • 3.1.1(Jul 20, 2016)

    • jquery.panzoom 3.0+ adds support for jQuery 3.0 (version number parity is coincidental). This included removing the jquery.event.pointertouch plugin–it broke since the event extension API had changed. The result was mainly that the originalEvent property got used more often.
    • I dropped support for pointer events because of pointer event issues in IE11. They weren't really adding anything and users were disabling pointer event support through hacks.

    New features

    1. contain: 'automatic' option. Greatly simplified contain option logic in the process.
    2. disableXAxis and disableYAxis options.
    3. panOnlyWhenZoomed option
    4. exponential zoom option - Zoom increments exponentially rather than linearly. This is somewhat experimental, but still enabled by default. Linear incrementation was just too slow.

    Notable bug fixes

    1. Fixed an issue where rotations with negative values were getting reset to 0.
    2. Moved backface-visibility:hidden style to parent to avoid blur on SVG images, but still promote the animation to its own composite layer for optimal performance.
    3. IE11 had a false positive on a feature test for the input event. IE11 now uses the change event fallback.

    Full 2.0.6 to 3.1.1 changelog

    Source code(tar.gz)
    Source code(zip)
  • 2.0.4(Apr 2, 2014)

    Settings transform styles is not supported in IE. Unfortunately, this means that transitions are not supported for SVG, but transforms work again in all browsers.

    See #86.

    Source code(tar.gz)
    Source code(zip)
  • 2.0.1(Mar 27, 2014)

    By default, whenever the focal option was used it disabled animations. It was meant to be used with pinch zooming and mousewheel zooming, which should not animate. However, I've seen some cases where focal has been used and animating is necessary.

    Migrating

    If you've been using the focal option with the mousewheel, make sure to set animate to false.

    $panzoom.parent().on('mousewheel.focal', function( e ) {
      e.preventDefault();
      var delta = e.delta || e.originalEvent.wheelDelta;
      var zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
      $panzoom.panzoom('zoom', zoomOut, {
        increment: 0.1,
        focal: e,
        animate: false
      });
    });
    

    Aside from this change, the latest updates to panzoom are also really exciting.

    1. Improved performance across the board. Every panzoom element now has its own compositor layer.
    2. Improved support for CommonJS loading.
    Source code(tar.gz)
    Source code(zip)
  • 1.8.2(Nov 20, 2013)

  • v1.4.1(Aug 12, 2013)

  • v1.4.0(Aug 12, 2013)

  • v1.3.8(Jul 18, 2013)

Owner
Timmy Willison
jQuery Core Team Lead, Front-End Lead at Spokestack
Timmy Willison
Create a deep copy of a set of matched elements with the dynamic state of all form elements copied to the cloned elements.

jq-deepest-copy FUNCTION: Create a deep copy of a set of matched elements while preserving the dynamic state of any matched form elements. Example Use

Michael Coughlin 5 Oct 28, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
A PostCSS plugin that transforms `css var` to `less var`.

PostCSS CSS var to Less var A PostCSS plugin to convert CSS variables to Less variables Installation npm install postcss-transform-css-var Examples /

cherry-man 9 Nov 28, 2022
VanillaSelectBox - A dropdown menu with lots of features that takes a select tag and transforms it into a single or multi-select menu with 1 or 2 levels

vanillaSelectBox v1.0.0 vanillaSelectBox : v1.00 : Adding a package.json file New : Possibility to change the dropdown tree and change the remote sear

philippe MEYER 103 Dec 16, 2022
A utility that mutates and transforms a style-dictionary object into something Figma Tokens plugin understands

Brought to you by Style Dictionary To Figma A utility that transforms a style-dictionary object into something Figma Tokens plugin understands. Used b

‹div›RIOTS 74 Jan 4, 2023
Lightweight and easy to use vanilla js library to add css animations to elements on scroll.

Scrollrisen Trigger css animations on scroll as elements enter the viewport. Zero dependencies, lightweight and easy to use and customize Installation

null 1 Oct 13, 2022
Discover a vast library of open-source Tailwind CSS components and Mobile UI design elements 🚀

SundarUI ?? Discover a vast library of open-source Tailwind CSS components and Mobile UI design elements ?? What is SundarUI? Sundar UI is a library o

Raj Patel 4 Mar 19, 2023
In this project, I built a to-do list app, which is inspired by the minimalist website. Build withHTML/CSS & JavaScript best practices: Correct use of tags, elements, properties and syntax.

Webpack Exercise In this project, I built a to-do list app, which is inspired by the minimalist website. Built With HTML/CSS & JavaScript best practic

Vanessa Oliveros 3 Oct 11, 2022
A super tiny Javascript library to make DOM elements draggable and movable. ~500 bytes and no dependencies.

dragmove.js A super tiny Javascript library to make DOM elements draggable and movable. Has touch screen support. Zero dependencies and 500 bytes Gzip

Kailash Nadh 814 Dec 29, 2022
DoMe is a ToDo App. you can add, delete and reorder elements of the todo list using drag and drop. You can also toggle between dark&light mode

DO ME Todo App Live Preview : DO ME Built With : - ReactJS - TailwindCSS Make sure you have: - Git - Nodejs version 14 or higher (we recommend using

Medjahdi Islem 5 Nov 18, 2022
A Drag-and-Drop library for all JavaScript frameworks implementing an enhanced transformation mechanism to manipulate DOM elements

JavaScript Project to Manipulate DOM Elements DFlex A Drag-and-Drop library for all JavaScript frameworks implementing an enhanced transformation mech

DFlex 1.5k Jan 8, 2023
Tiny js library to make DOM elements movable and resizable .

resizedrag.js Tiny js library to make DOM elements movable and resizable . Demo Here . This library has added resizing functionalities to the existing

null 4 Mar 28, 2022
A JavaScript library to create html elements with js easily

applecake is a javascript library for making HTML elements with javascript really easy . Why applecake ? Really easy to use It is not heavy It has a s

null 4 Jul 21, 2021
curtains.js is a lightweight vanilla WebGL javascript library that turns HTML DOM elements into interactive textured planes.

What is it ? Shaders are the new front-end web developpment big thing, with the ability to create very powerful 3D interactions and animations. A lot

Martin Laxenaire 1.4k Jan 1, 2023
This experimental library patches the global custom elements registry to allow re-defining or reload a custom element.

Redefine Custom Elements This experimental library patches the global custom elements registry to allow re-defining a custom element. Based on the spe

Caridy Patiño 21 Dec 11, 2022
A JavaScript library for adding ripple effects to HTML elements based on mouse events.

About project Ripplejs is an open source javascript library created for the purpose of adding ripple effects to html elements based on mouse events. L

Udezue Oluomachi Chimaobi 7 May 10, 2022
A ScrollSpy library for detecting enter/exit of elements in the viewport when the user scrolls

jquery-scrollspy A jQuery plugin for detecting enter/exit of elements in the viewport when the user scrolls. New Features Added a couple new features:

John Smart 276 Jul 1, 2022