diff --git a/.gitignore b/.gitignore
index 88f6afd..460e5a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@ node_modules/
**/*.orig
.DS_Store
npm-debug.log
+dist/*.*
+dist/svg-pan-zoom.js
diff --git a/demo/inline.html b/demo/inline.html
index 190a40a..63446b4 100644
--- a/demo/inline.html
+++ b/demo/inline.html
@@ -735,7 +735,9 @@
-
+
+
+
diff --git a/demo/simple-animation.html b/demo/simple-animation.html
index 7e9d69d..073356c 100644
--- a/demo/simple-animation.html
+++ b/demo/simple-animation.html
@@ -35,6 +35,7 @@
fit: true,
center: true,
minZoom: 0.1
+
});
// Zoom out
@@ -52,7 +53,7 @@
intervalID = setInterval(function(){
if (animationStep++ < animationSteps) {
panZoomInstance.panBy({x: stepX, y: stepY});
- panZoomInstance.rotate(panZoomInstance.getRotate()+5);
+ panZoomInstance.setCenterRotationAngle(panZoomInstance.getCenterRotationAngle()+5);
} else {
// Cancel interval
diff --git a/dist/svg-pan-zoom.js b/dist/svg-pan-zoom.js
index bf6c982..54005bc 100644
--- a/dist/svg-pan-zoom.js
+++ b/dist/svg-pan-zoom.js
@@ -221,8 +221,8 @@ ShadowViewport.prototype.init = function(viewport, options) {
this.options = options;
// State cache
- this.originalState = { zoom: 1, x: 0, y: 0, rotate:0 };
- this.activeState = { zoom: 1, x: 0, y: 0, rotate:0 };
+ this.originalState = { zoom: 1, x: 0, y: 0, angle: 0 };
+ this.activeState = { zoom: 1, x: 0, y: 0, angle: 0 };
this.updateCTMCached = Utils.proxy(this.updateCTM, this);
@@ -411,36 +411,42 @@ ShadowViewport.prototype.getPan = function() {
return { x: this.activeState.x, y: this.activeState.y };
};
/**
- * Get rotate
+ * Get rotation angle
*
* @return {Float} angle
*/
-ShadowViewport.prototype.getRotate = function() {
- return this.activeState.rotate
-}
+ShadowViewport.prototype.getAngle = function() {
+ return this.activeState.angle;
+};
/**
- * Get rotate transformation
+ * Get rotation
*
* @return {Object} angle and point of rotation
*/
-ShadowViewport.prototype.getRotateTransform = function() {
+ShadowViewport.prototype.getRotation = function() {
+ // console.log("a", this.getAngle());
+ if (this.getAngle() === 0) {
+ return null;
+ }
+
return {
- angle: this.getRotate(),
+ angle: this.getAngle(),
x: this.getViewBox().width / 2,
y: this.getViewBox().height / 2
- }
-}
+ };
+};
/**
- * Set rotate
+ * Set angle
*
* @param {Float} angle
*/
-ShadowViewport.prototype.rotate = function(angle) {
- this.activeState.rotate = angle;
- this.updateCTMOnNextFrame()
-}
+ShadowViewport.prototype.setAngle = function(angle) {
+ this.activeState.angle = angle;
+ this.updateCTMOnNextFrame();
+ // console.log("new angle", angle, this.activeState);
+};
/**
* Return cached viewport CTM value that can be safely modified
@@ -583,9 +589,11 @@ ShadowViewport.prototype.updateCTMOnNextFrame = function() {
*/
ShadowViewport.prototype.updateCTM = function() {
var ctm = this.getCTM();
+ var rotation = this.getRotation();
+ // console.log("updateCTM", ctm, rotation);
// Updates SVG element
- SvgUtils.setCTM(this.viewport, ctm, this.defs, this.getRotateTransform());
+ SvgUtils.setCTM(this.viewport, ctm, this.defs, rotation);
// Free the lock
this.pendingUpdate = false;
@@ -651,7 +659,7 @@ var optionsDefaults = {
beforePan: null,
onPan: null,
beforeRotate: null,
- onRotate:null,
+ onRotate: null,
customEventsHandler: null,
eventsListenerElement: null,
onUpdatedCTM: null
@@ -1025,27 +1033,18 @@ SvgPanZoom.prototype.publicZoomAtPoint = function(scale, point, absolute) {
*
* @param {Float} angle
*/
-SvgPanZoom.prototype.rotate = function(angle) {
- this.viewport.rotate(angle)
-}
-
-/**
- * Rotate relative
- *
- * @param {Float} relative angle
- */
-SvgPanZoom.prototype.rotateRelative = function(angle) {
- this.rotate(this.getRotate() + angle)
-}
+SvgPanZoom.prototype.setAngle = function(angle) {
+ this.viewport.setAngle(angle);
+};
/**
* Get rotate for public usage
*
* @return {Float} rotate
*/
-SvgPanZoom.prototype.getRotate = function() {
- return this.viewport.getRotate()
-}
+SvgPanZoom.prototype.getAngle = function() {
+ return this.viewport.getAngle();
+};
/**
* Get zoom scale
@@ -1091,18 +1090,18 @@ SvgPanZoom.prototype.resetPan = function() {
this.pan(this.viewport.getOriginalState());
};
/**
- * Set rotate to initial state
+ * Set rotation to initial state
*/
-SvgPanZoom.prototype.resetRotate = function() {
- this.rotate(this.viewport.getOriginalState().rotate);
-}
+SvgPanZoom.prototype.resetRotation = function() {
+ this.setAngle(this.viewport.getOriginalState().angle);
+};
/**
* Set pan and zoom to initial state
*/
SvgPanZoom.prototype.reset = function() {
this.resetZoom();
this.resetPan();
- this.resetRotate();
+ this.resetRotation();
};
/**
@@ -1550,16 +1549,14 @@ SvgPanZoom.prototype.getPublicInstance = function() {
},
getZoom: function() {
return that.getRelativeZoom();
- },
- rotate: function(angle) {
- that.rotate(angle); return that.pi
- },
- rotateRelative: function(angle) {
- that.rotateRelative(angle); return that.pi
- },
- getRotate: function() {
- return that.getRotate()
- },
+ },
+ setCenterRotationAngle: function(angle) {
+ that.setAngle(angle);
+ return that.pi;
+ },
+ getCenterRotationAngle: function() {
+ return that.getAngle();
+ },
// CTM update
setOnUpdatedCTM: function(fn) {
that.options.onUpdatedCTM =
@@ -1575,8 +1572,9 @@ SvgPanZoom.prototype.getPublicInstance = function() {
that.resetPan();
return that.pi;
},
- resetRotate: function() {
- that.resetPan(); return that.pi
+ resetRotation: function() {
+ that.resetRotation();
+ return that.pi;
},
reset: function() {
that.reset();
@@ -1609,8 +1607,8 @@ SvgPanZoom.prototype.getPublicInstance = function() {
width: that.width,
height: that.height,
realZoom: that.getZoom(),
- viewBox: that.viewport.getViewBox(),
- rotate: that.getRotate()
+ angle: that.getAngle(),
+ viewBox: that.viewport.getViewBox()
};
},
// Destroy
@@ -1810,6 +1808,19 @@ module.exports = {
this ? this.internetExplorerRedisplayInterval : null
),
+ getMatrix: function(m, rotation){
+ if(rotation == null)
+ return m;
+ var deg2radians = Math.PI * 2 / 360;
+ return{
+ a : m.a * Math.cos(rotation.angle * deg2radians),
+ b : m.a * Math.sin(rotation.angle * deg2radians),
+ c : m.d * Math.sin(-rotation.angle * deg2radians),
+ d : m.d * Math.cos(-rotation.angle * deg2radians),
+ e : m.e,
+ f : m.f
+ };
+ },
/**
* Sets the current transform matrix of an element
*
@@ -1817,7 +1828,10 @@ module.exports = {
* @param {SVGMatrix} matrix CTM
* @param {SVGElement} defs
*/
- setCTM: function(element, matrix, defs, rotate) {
+ setCTM: function(element, matrix, defs, rotation) {
+
+
+ var rotatedMatrix = this.getMatrix(matrix, rotation);
var that = this,
s =
"matrix(" +
@@ -1832,20 +1846,25 @@ module.exports = {
matrix.e +
"," +
matrix.f +
- ")";
- if(rotate != 0){
- s += ' rotate(' + rotate.angle + ',' + rotate.x + ',' + rotate.y + ')';
- }
-
- element.setAttributeNS(null, "transform", s);
+ ")",
+ cssTransform = s;
+ var svgString ="scale("+matrix.a+" "+matrix.d+") translate("+matrix.e+" "+matrix.f+")";
+ cssTransform = "scaleX("+matrix.a+") scaleY("+matrix.d+") translate("+matrix.e+"px,"+matrix.f+"px)";
+ if(rotation != null){
+ svgString += " rotate("+rotation.angle+" "+rotation.x+" "+rotation.y+")";
+ cssTransform += " rotate("+rotation.angle+"deg)";
+ }
+ // var composedString = "scale("+1+","+1+") translateX("+matrix.e+") translateY("+matrix.f+");";
+ element.setAttributeNS(null, "transform", svgString);
+
if ("transform" in element.style) {
- element.style.transform = s;
+ element.style.transform = cssTransform;
} else if ("-ms-transform" in element.style) {
- element.style["-ms-transform"] = s;
+ element.style["-ms-transform"] = cssTransform;
} else if ("-webkit-transform" in element.style) {
- element.style["-webkit-transform"] = s;
+ element.style["-webkit-transform"] = cssTransform;
}
-
+
// IE has a bug that makes markers disappear on zoom (when the matrix "a" and/or "d" elements change)
// see http://stackoverflow.com/questions/17654578/svg-marker-does-not-work-in-ie9-10
// and http://srndolha.wordpress.com/2013/11/25/svg-line-markers-may-disappear-in-internet-explorer-11/
@@ -1860,7 +1879,6 @@ module.exports = {
}, that.internetExplorerRedisplayInterval);
}
},
-
/**
* Instantiate an SVGPoint object with given event coordinates
*
diff --git a/gulpfile.js b/gulpfile.js
index c0e3321..53551ef 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -27,27 +27,33 @@ function isFixed(file) {
* Build script
*/
function compile() {
- return browserify({ entries: "./src/stand-alone.js" })
- .bundle()
- .on("error", function(err) {
- console.log(err.toString());
- this.emit("end");
- })
- .pipe(source("svg-pan-zoom.js"))
- .pipe(buffer())
- .pipe(header(banner, { pkg: pkg }))
- .pipe(gulp.dest("./dist/"))
- .pipe(rename("svg-pan-zoom.min.js"))
- .pipe(uglify())
- .pipe(header(banner, { pkg: pkg }))
- .pipe(gulp.dest("./dist/"));
+ return (
+ browserify({ entries: "./src/stand-alone.js" })
+ .bundle()
+ .on("error", function(err) {
+ console.log(err.toString());
+ this.emit("end");
+ })
+ .pipe(source("svg-pan-zoom.js"))
+ .pipe(buffer())
+ .pipe(header(banner, { pkg: pkg }))
+ .pipe(gulp.dest("./dist/"))
+ .pipe(rename("svg-pan-zoom.min.js"))
+ .pipe(uglify())
+ // Catch minification errors
+ // .on("error", function(err) {
+ // console.log(err.toString());
+ // this.emit("end");
+ // })
+ .pipe(header(banner, { pkg: pkg }))
+ );
}
/**
* Watch script
*/
function watch() {
- return gulp.watch("./src/**/*.js", gulp.series("compile"));
+ return gulp.watch("./src/**/*.js", gulp.series([compile]));
}
/**
diff --git a/src/shadow-viewport.js b/src/shadow-viewport.js
index c723a8f..0a1db2d 100644
--- a/src/shadow-viewport.js
+++ b/src/shadow-viewport.js
@@ -17,8 +17,8 @@ ShadowViewport.prototype.init = function(viewport, options) {
this.options = options;
// State cache
- this.originalState = { zoom: 1, x: 0, y: 0, rotate:0 };
- this.activeState = { zoom: 1, x: 0, y: 0, rotate:0 };
+ this.originalState = { zoom: 1, x: 0, y: 0, angle: 0 };
+ this.activeState = { zoom: 1, x: 0, y: 0, angle: 0 };
this.updateCTMCached = Utils.proxy(this.updateCTM, this);
@@ -207,36 +207,42 @@ ShadowViewport.prototype.getPan = function() {
return { x: this.activeState.x, y: this.activeState.y };
};
/**
- * Get rotate
+ * Get rotation angle
*
* @return {Float} angle
*/
-ShadowViewport.prototype.getRotate = function() {
- return this.activeState.rotate
-}
+ShadowViewport.prototype.getAngle = function() {
+ return this.activeState.angle;
+};
/**
- * Get rotate transformation
+ * Get rotation
*
* @return {Object} angle and point of rotation
*/
-ShadowViewport.prototype.getRotateTransform = function() {
+ShadowViewport.prototype.getRotation = function() {
+ // console.log("a", this.getAngle());
+ if (this.getAngle() === 0) {
+ return null;
+ }
+
return {
- angle: this.getRotate(),
+ angle: this.getAngle(),
x: this.getViewBox().width / 2,
y: this.getViewBox().height / 2
- }
-}
+ };
+};
/**
- * Set rotate
+ * Set angle
*
* @param {Float} angle
*/
-ShadowViewport.prototype.rotate = function(angle) {
- this.activeState.rotate = angle;
- this.updateCTMOnNextFrame()
-}
+ShadowViewport.prototype.setAngle = function(angle) {
+ this.activeState.angle = angle;
+ this.updateCTMOnNextFrame();
+ // console.log("new angle", angle, this.activeState);
+};
/**
* Return cached viewport CTM value that can be safely modified
@@ -379,9 +385,11 @@ ShadowViewport.prototype.updateCTMOnNextFrame = function() {
*/
ShadowViewport.prototype.updateCTM = function() {
var ctm = this.getCTM();
+ var rotation = this.getRotation();
+ // console.log("updateCTM", ctm, rotation);
// Updates SVG element
- SvgUtils.setCTM(this.viewport, ctm, this.defs, this.getRotateTransform());
+ SvgUtils.setCTM(this.viewport, ctm, this.defs, rotation);
// Free the lock
this.pendingUpdate = false;
diff --git a/src/svg-pan-zoom.js b/src/svg-pan-zoom.js
index a846e45..7bfa3bb 100644
--- a/src/svg-pan-zoom.js
+++ b/src/svg-pan-zoom.js
@@ -28,7 +28,7 @@ var optionsDefaults = {
beforePan: null,
onPan: null,
beforeRotate: null,
- onRotate:null,
+ onRotate: null,
customEventsHandler: null,
eventsListenerElement: null,
onUpdatedCTM: null
@@ -402,27 +402,18 @@ SvgPanZoom.prototype.publicZoomAtPoint = function(scale, point, absolute) {
*
* @param {Float} angle
*/
-SvgPanZoom.prototype.rotate = function(angle) {
- this.viewport.rotate(angle)
-}
-
-/**
- * Rotate relative
- *
- * @param {Float} relative angle
- */
-SvgPanZoom.prototype.rotateRelative = function(angle) {
- this.rotate(this.getRotate() + angle)
-}
+SvgPanZoom.prototype.setAngle = function(angle) {
+ this.viewport.setAngle(angle);
+};
/**
* Get rotate for public usage
*
* @return {Float} rotate
*/
-SvgPanZoom.prototype.getRotate = function() {
- return this.viewport.getRotate()
-}
+SvgPanZoom.prototype.getAngle = function() {
+ return this.viewport.getAngle();
+};
/**
* Get zoom scale
@@ -468,18 +459,18 @@ SvgPanZoom.prototype.resetPan = function() {
this.pan(this.viewport.getOriginalState());
};
/**
- * Set rotate to initial state
+ * Set rotation to initial state
*/
-SvgPanZoom.prototype.resetRotate = function() {
- this.rotate(this.viewport.getOriginalState().rotate);
-}
+SvgPanZoom.prototype.resetRotation = function() {
+ this.setAngle(this.viewport.getOriginalState().angle);
+};
/**
* Set pan and zoom to initial state
*/
SvgPanZoom.prototype.reset = function() {
this.resetZoom();
this.resetPan();
- this.resetRotate();
+ this.resetRotation();
};
/**
@@ -927,16 +918,14 @@ SvgPanZoom.prototype.getPublicInstance = function() {
},
getZoom: function() {
return that.getRelativeZoom();
- },
- rotate: function(angle) {
- that.rotate(angle); return that.pi
- },
- rotateRelative: function(angle) {
- that.rotateRelative(angle); return that.pi
- },
- getRotate: function() {
- return that.getRotate()
- },
+ },
+ setCenterRotationAngle: function(angle) {
+ that.setAngle(angle);
+ return that.pi;
+ },
+ getCenterRotationAngle: function() {
+ return that.getAngle();
+ },
// CTM update
setOnUpdatedCTM: function(fn) {
that.options.onUpdatedCTM =
@@ -952,8 +941,9 @@ SvgPanZoom.prototype.getPublicInstance = function() {
that.resetPan();
return that.pi;
},
- resetRotate: function() {
- that.resetPan(); return that.pi
+ resetRotation: function() {
+ that.resetRotation();
+ return that.pi;
},
reset: function() {
that.reset();
@@ -986,8 +976,8 @@ SvgPanZoom.prototype.getPublicInstance = function() {
width: that.width,
height: that.height,
realZoom: that.getZoom(),
- viewBox: that.viewport.getViewBox(),
- rotate: that.getRotate()
+ angle: that.getAngle(),
+ viewBox: that.viewport.getViewBox()
};
},
// Destroy
diff --git a/src/svg-utilities.js b/src/svg-utilities.js
index 8a1daf8..506ddde 100644
--- a/src/svg-utilities.js
+++ b/src/svg-utilities.js
@@ -147,6 +147,19 @@ module.exports = {
this ? this.internetExplorerRedisplayInterval : null
),
+ getMatrix: function(m, rotation){
+ if(rotation == null)
+ return m;
+ var deg2radians = Math.PI * 2 / 360;
+ return{
+ a : m.a * Math.cos(rotation.angle * deg2radians),
+ b : m.a * Math.sin(rotation.angle * deg2radians),
+ c : m.d * Math.sin(-rotation.angle * deg2radians),
+ d : m.d * Math.cos(-rotation.angle * deg2radians),
+ e : m.e,
+ f : m.f
+ };
+ },
/**
* Sets the current transform matrix of an element
*
@@ -154,7 +167,10 @@ module.exports = {
* @param {SVGMatrix} matrix CTM
* @param {SVGElement} defs
*/
- setCTM: function(element, matrix, defs, rotate) {
+ setCTM: function(element, matrix, defs, rotation) {
+
+
+ var rotatedMatrix = this.getMatrix(matrix, rotation);
var that = this,
s =
"matrix(" +
@@ -169,20 +185,25 @@ module.exports = {
matrix.e +
"," +
matrix.f +
- ")";
- if(rotate != 0){
- s += ' rotate(' + rotate.angle + ',' + rotate.x + ',' + rotate.y + ')';
- }
-
- element.setAttributeNS(null, "transform", s);
+ ")",
+ cssTransform = s;
+ var svgString ="scale("+matrix.a+" "+matrix.d+") translate("+matrix.e+" "+matrix.f+")";
+ cssTransform = "scaleX("+matrix.a+") scaleY("+matrix.d+") translate("+matrix.e+"px,"+matrix.f+"px)";
+ if(rotation != null){
+ svgString += " rotate("+rotation.angle+" "+rotation.x+" "+rotation.y+")";
+ cssTransform += " rotate("+rotation.angle+"deg)";
+ }
+ // var composedString = "scale("+1+","+1+") translateX("+matrix.e+") translateY("+matrix.f+");";
+ element.setAttributeNS(null, "transform", svgString);
+
if ("transform" in element.style) {
- element.style.transform = s;
+ element.style.transform = cssTransform;
} else if ("-ms-transform" in element.style) {
- element.style["-ms-transform"] = s;
+ element.style["-ms-transform"] = cssTransform;
} else if ("-webkit-transform" in element.style) {
- element.style["-webkit-transform"] = s;
+ element.style["-webkit-transform"] = cssTransform;
}
-
+
// IE has a bug that makes markers disappear on zoom (when the matrix "a" and/or "d" elements change)
// see http://stackoverflow.com/questions/17654578/svg-marker-does-not-work-in-ie9-10
// and http://srndolha.wordpress.com/2013/11/25/svg-line-markers-may-disappear-in-internet-explorer-11/
@@ -197,7 +218,6 @@ module.exports = {
}, that.internetExplorerRedisplayInterval);
}
},
-
/**
* Instantiate an SVGPoint object with given event coordinates
*
diff --git a/tests/test_api.js b/tests/test_api.js
index 446424a..666129d 100644
--- a/tests/test_api.js
+++ b/tests/test_api.js
@@ -622,43 +622,42 @@ test("reset (zoom and pan)", function() {
});
/**
- * Rotate
+ * Rotate
*/
-test("rotate", function(){
+test("rotate", function() {
expect(1);
instance = initSvgPanZoom();
- instance.rotate(45);
- deepEqual(instance.getRotate(), 45);
+ instance.setCenterRotationAngle(45);
+ deepEqual(instance.getCenterRotationAngle(), 45);
});
-test("rotateRelative", function(){
+test("rotateRelative", function() {
expect(2);
instance = initSvgPanZoom();
- instance.rotate(45);
- deepEqual(instance.getRotate(), 45);
- instance.rotateRelative(45);
- deepEqual(instance.getRotate(), 90);
-
+ instance.setCenterRotationAngle(45);
+ deepEqual(instance.getCenterRotationAngle(), 45);
+ instance.setCenterRotationAngle(90);
+ deepEqual(instance.getCenterRotationAngle(), 90);
});
-test("resetRotate", function(){
+test("resetRotation", function() {
expect(2);
instance = initSvgPanZoom();
- var initialRotate = instance.getRotate();
- instance.rotate(45);
- deepEqual(instance.getRotate(), 45);
- instance.resetRotate();
- deepEqual(instance.getRotate(), 45);//need test where resetRotate is tested before original object is adjusted... it still weird that the normal reset does work...
+ var initialRotate = instance.getCenterRotationAngle();
+ instance.setCenterRotationAngle(45);
+ deepEqual(instance.getCenterRotationAngle(), 45);
+ instance.resetRotation();
+ deepEqual(instance.getCenterRotationAngle(), 0);
});
-test("global reset", function(){
+test("global reset", function() {
expect(1);
instance = initSvgPanZoom();
- var initialRotate = instance.getRotate();
- instance.rotate(45);
+ var initialRotate = instance.getCenterRotationAngle();
+ instance.setCenterRotationAngle(45);
instance.reset();
- deepEqual(instance.getRotate(), initialRotate);
+ deepEqual(instance.getCenterRotationAngle(), initialRotate);
});
/**