Simple pan/zoom solution for SVGs in HTML. It adds events listeners for mouse scroll, double-click and pan, plus it optionally offers:
- JavaScript API for control of pan and zoom behavior
- onPan and onZoom event handlers
- On-screen zoom controls
It works cross-browser and supports both inline SVGs and SVGs in HTML 'object' or 'embed' elements.
If you're looking for version 2.3.x you can find it in v2.3.x branch
If you found a bug or have a suggestion first check if there is a similar open or closed issue. If there are none then create a new one.
When oppening a new issue please provide a reproductible example:
- Share it so we could get directly to the problem. You can use this starter jsfiddle setup to provide your example. Or upload your own jsfiddle.net (or any other live) example.
- Mention your library version (located in library file in header)
- Mention your browser name, version and operating system
- Mention any other important for debug details
If you solved a bug or implemented a feature that may be useful for others then you're welcome to create a pull request.
If you have any other type of questions, problems, your code is not working or you want to critique the library - you can use StackOverflow. Just tag you question with svgpanzoom
.
Best way to contribute is to create a pull request. In order to create a pull request:
- Fork this repository
- Clone repository fork (created in previous step) locally (on your machine)
- Ensure that you have nodejs and npm installed locally
- In console:
cd
into project folder- Run
npm install
- Run
npm install -g gulp
if you don't have it already installed globally - Running
gulp
will listen for source files changes (insrc
folder) and will automatically build distribution files - Running
gulp browserify
will compile source files - Running
gulp check
for some syntax errors - Running
gulp test
will run tests - Running
gulp build
will prepare the project for a new release
- Implement the change using
gulp
orgulp browserify
- After change is done test it with
gulp check
andgulp test
- Commit only meaningful changes. Do not commit distribution files (
dist
folder). Distribution files are built only before a release - Push your changes into your fork
- Create a pull request
Pan and zoom the SVG tiger on github pages:
- Single Inline SVG
- Multiple Inline SVGs
- SVG Inserted with 'Embed' Element
- SVG Inserted with 'Object' Element
- SVG Inserted with 'Img' Element (These cannot be panned/zoomed.)
- SVG With custom controls
- Resize SVG container on document resize
- Two SVGs with sinchronized zooming and panning
- Custom events: Touch events support: pan, double tap, pinch
- Custom events: Enable zooming only on click, disable on mouse out
- Limit pan
- Dynamic SVG load
- Using Require.js
- Pan animation
Reference the svg-pan-zoom.js file from your HTML document. Then call the init method:
var panZoomTiger = svgPanZoom('#demo-tiger');
// or
var svgElement = document.querySelector('#demo-tiger')
var panZoomTiger = svgPanZoom(svgElement)
First argument to function should be a CSS selector of SVG element or a DOM Element.
If you want to override the defaults, you can optionally specify one or more arguments:
svgPanZoom('#demo-tiger', {
viewportSelector: '.svg-pan-zoom_viewport'
, panEnabled: true
, controlIconsEnabled: false
, zoomEnabled: true
, dblClickZoomEnabled: true
, mouseWheelZoomEnabled: true
, preventMouseEventsDefault: true
, zoomScaleSensitivity: 0.2
, minZoom: 0.5
, maxZoom: 10
, fit: true
, contain: false
, center: true
, refreshRate: 'auto'
, beforeZoom: function(){}
, onZoom: function(){}
, beforePan: function(){}
, onPan: function(){}
, customEventsHandler: {}
});
If any arguments are specified, they must have the following value types:
- 'viewportSelector' can be querySelector string or SVGElement.
- 'panEnabled' must be true or false. Default is true.
- 'controlIconsEnabled' must be true or false. Default is false.
- 'zoomEnabled' must be true or false. Default is true.
- 'dblClickZoomEnabled' must be true or false. Default is true.
- 'mouseWheelZoomEnabled' must be true or false. Default is true.
- 'preventMouseEventsDefault' must be true or false. Default is true.
- 'zoomScaleSensitivity' must be a scalar. Default is 0.2.
- 'minZoom' must be a scalar. Default is 0.5.
- 'maxZoom' must be a scalar. Default is 10.
- 'fit' must be true or false. Default is true.
- 'contain' must be true or false. Default is false.
- 'center' must be true or false. Default is true.
- 'refreshRate' must be a number or 'auto'
- 'beforeZoom' must be a callback function to be called before zoom changes.
- 'onZoom' must be a callback function to be called when zoom changes.
- 'beforePan' must be a callback function to be called before pan changes.
- 'onPan' must be a callback function to be called when pan changes.
- 'customEventsHandler' must be a object with
init
anddestroy
arguments as functions.
beforeZoom
will be called with 2 float attributes: oldZoom and newZoom.
If beforeZoom
will return false
then zooming will be halted.
onZoom
callbacks will be called with one float attribute representing new zoom scale.
beforePan
will be called with 2 attributes:
oldPan
newPan
Each of this objects has two attributes (x and y) representing current pan (on X and Y axes).
If beforePan
will return false
or an object {x: true, y: true}
then panning will be halted.
If you want to prevent panning only on one axis then return a object of type {x: true, y: false}
.
You can alter panning on X and Y axes by providing alternative values through return {x: 10, y: 20}
.
Caution! If you alter panning by returning custom values
{x: 10, y: 20}
it will update only current pan step. If panning is done by mouse/touch you have to take in account that next pan step (after the one that you altered) will be performed with values that do not consider altered values (as they even did not existed).
onPan
callback will be called with one attribute: newPan
.
Caution! Calling zoom or pan API methods form inside of
beforeZoom
,onZoom
,beforePan
andonPan
callbacks may lead to infinite loop.
panEnabled
and zoomEnabled
are related only to user interaction. If any of this options are disabled - you still can zoom and pan via API.
fit
takes preceeding to contain
. So if you set fit: true
then contain
's value doesn't matter.
You may want to use a custom viewport if you have more layers in your SVG but you want to pan-zoom only one of them.
By default if:
- There is just one top-level graphical element of type SVGGElement (
<g>
) - SVGGElement has no
transform
attribute - There is no other SVGGElement with class name
svg-pan-zoom_viewport
then the top-level graphical element will be used as viewport.
To specify which layer (SVGGElement) should be pan-zoomed set the svg-pan-zoom_viewport
class name to that element:
<g class="svg-pan-zoom_viewport"></g>
.
Do not set any transform attributes to that element. It will make the library misbehave. If you need transform attribute for viewport better create a nested group element and set transforms to that element:
<g class="svg-pan-zoom_viewport">
<g transform="matrix(1,0,0,1,0,0);"></g>
</g>
You can specify your own viewport selector by altering viewportSelector
config value:
svgPanZoom('#demo-tiger', {
viewportSelector: '.svg-pan-zoom_viewport'
});
// or
var viewportGroupElement = document.getElemenById('demo-tiger').querySelector('.svg-pan-zoom_viewport');
svgPanZoom('#demo-tiger', {
viewportSelector: viewportGroupElement
});
If you do use browserify in your project you may do it by:
- Add the package as node module
npm install --save ariutta/svg-pan-zoom
- Require svg-pan-zoom in your source file
svgPanZoom = require('svg-pan-zoom')
- Use in the same way as you would do with global svgPanZoom:
instance = svgPanZoom('#demo-tiger')
An example of how to load library using Require.js is available in demo/require.html
You may want to add custom events support (for example double tap or pinch).
It is possible by setting customEventsHandler
configuration option.
customEventsHandler
should be an object with following attributes:
haltEventListeners
: array of stringsinit
: functiondestroy
: function
haltEventListeners
specifies which default event listeners should be disabled (in order to avoid conflicts as svg-pan-zoom supports by default panning using touch events).
init
is a function that is called when svg-pan-zoom is initialized. A object is passed into this function.
Passed object has following attributes:
svgElement
- SVGSVGElementinstance
- svg-pan-zoom public API instance
destroy
is a function called upon svg-pan-zoom destroy
An example of how to use it together with Hammer.js:
var options = {
zoomEnabled: true
, controlIconsEnabled: true
, customEventsHandler: {
// Halt all touch events
haltEventListeners: ['touchstart', 'touchend', 'touchmove', 'touchleave', 'touchcancel']
// Init custom events handler
, init: function(options) {
// Init Hammer
this.hammer = Hammer(options.svgElement)
// Handle double tap
this.hammer.on('doubletap', function(ev){
options.instance.zoomIn()
})
}
// Destroy custom events handler
, destroy: function(){
this.hammer.destroy()
}
}
}
svgPanZoom('#mobile-svg', options);
You may find an example that adds support for Hammer.js pan, pinch and doubletap in demo/mobile.html
You may want to keep SVG content visible by not allowing panning over SVG borders.
To do so you may prevent or alter panning from beforePan
callback. For more details take a look at demo/limit-pan.html
example.
When you call svgPanZoom
method it returns an object with following methods:
- enablePan
- disablePan
- isPanEnabled
- pan
- panBy
- getPan
- setBeforePan
- setOnPan
- enableZoom
- disableZoom
- isZoomEnabled
- enableControlIcons
- disableControlIcons
- isControlIconsEnabled
- enableDblClickZoom
- disableDblClickZoom
- isDblClickZoomEnabled
- enableMouseWheelZoom
- disableMouseWheelZoom
- isMouseWheelZoomEnabled
- setZoomScaleSensitivity
- setMinZoom
- setMaxZoom
- setBeforeZoom
- setOnZoom
- zoom
- zoomBy
- zoomAtPoint
- zoomAtPointBy
- zoomIn
- zoomOut
- getZoom
- resetZoom
- resetPan
- reset
- fit
- contain
- center
- updateBBox
- resize
- getSizes
- destroy
To programmatically pan, call the pan method with vector as first argument:
// Get instance
var panZoomTiger = svgPanZoom('#demo-tiger');
// Pan to rendered point x = 50, y = 50
panZoomTiger.pan({x: 50, y: 50})
// Pan by x = 50, y = 50 of rendered pixels
panZoomTiger.panBy({x: 50, y: 50})
To programmatically zoom, you can use the zoom method to specify your desired scale value:
// Get instance
var panZoomTiger = svgPanZoom('#demo-tiger');
// Set zoom level to 2
panZoomTiger.zoom(2)
// Zoom by 130%
panZoomTiger.zoomBy(1.3)
// Set zoom level to 2 at point
panZoomTiger.zoomAtPoint(2, {x: 50, y: 50})
// Zoom by 130% at given point
panZoomTiger.zoomAtPointBy(1.3, {x: 50, y: 50})
Zoom is relative to initial SVG internal zoom level. If your SVG was fit at the begging (option fit: true) and thus zoomed in or out to fit available space - initial scale will be anyway 1.
Or you can use the zoomIn or zoomOut methods:
// Get instance
var panZoomTiger = svgPanZoom('#demo-tiger');
panZoomTiger.zoomIn()
panZoomTiger.zoomOut()
panZoomTiger.resetZoom()
If you want faster or slower zooming, you can override the default zoom increment with the setZoomScaleSensitivity method.
To programmatically enable/disable pan or zoom:
// Get instance
var panZoomTiger = svgPanZoom('#demo-tiger');
panZoomTiger.enablePan();
panZoomTiger.disablePan();
panZoomTiger.enableZoom();
panZoomTiger.disableZoom();
To fit and center (you may try contain
instead of fit
):
// Get instance
var panZoomTiger = svgPanZoom('#demo-tiger');
panZoomTiger.fit();
panZoomTiger.center();
If you want to fit and center your SVG after its container resize:
// Get instance
var panZoomTiger = svgPanZoom('#demo-tiger');
panZoomTiger.resize(); // update SVG cached size and controls positions
panZoomTiger.fit();
panZoomTiger.center();
If you update SVG (viewport) contents so its border box (virtual box that contains all elements) changes you have to call updateBBox
:
var panZoomTiger = svgPanZoom('#demo-tiger');
panZoomTiger.fit();
// Update SVG rectangle width
document.getElementById('demo-tiger').querySelector('rect').setAttribute('width', 200)
// fit does not work right anymore as viewport bounding box changed
panZoomTiger.fit();
panZoomTiger.updateBBox(); // Update viewport bounding box
panZoomTiger.fit(); // fit works as expected
If you need more data about SVG you can call getSizes
. It will return an object that will contain:
width
- SVG cached widthheight
- SVG cached heightrealZoom
- a and d attributes of transform matrix applied over viewportviewBox
- an object containing cached sizes of viewport boxder boxwidth
height
x
- x offsety
- y offset
Destroy SvgPanZoom instance:
// Get instance
var panZoomTiger = svgPanZoom('#demo-tiger');
panZoomTiger.destroy();
delete panZoomTiger;
Before committing you should check your code style by running gulp check
.
If you made a change then first build the library. Open ./tests/index.html
in your browser. All tests should pass.
If you have PhantomJS installed then you can run gulp test
.
- Chrome
- Firefox
- Safari
- Opera
- Internet Explorer 9+ (works badly if viewBox attribute is set)
This library used the SVGPan library as a starting point. SVGPan is intended for use with the SVG 'script' element, whereas svg-pan-zoom is intended for use with the HTML 'script' element.
The code from the SVGPan library is licensed under the following BSD license:
Copyright 2009-2010 Andrea Leofreddi <[email protected]>. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of Andrea Leofreddi.
The code from the updates and changes to SVGPan are licensed under the same BSD license, with the copyright for the code from each change held by the author of that code.