Using the sample code from Konvajs.org as a base (https://konvajs.org/docs/sandbox/Multi-touch_Scale_Stage.html), I have added a large SVG to a layer (4096 x 3444) to experiment with zoom / pan of a vector-based map, base64 encoded SVG in this instance. Initial impressions are good however during testing I experience an odd bug where during a pinch the view of the map would snap to a different location on the map not the area that I centred on.
Here is the code (map base64 code removed due to length):
// by default Konva prevent some events when node is dragging
// it improve the performance and work well for 95% of cases
// we need to enable all events on Konva, even when we are dragging a node
// so it triggers touchmove correctly
Konva.hitOnDragEnabled = true;
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
draggable: true,
});
var layer = new Konva.Layer();
var triangle = new Konva.RegularPolygon({
x: 190,
y: stage.height() / 2,
sides: 3,
radius: 80,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
});
var circle = new Konva.Circle({
x: 380,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
let bg = new Konva.Image({
width: 4096,
height: 3444
});
layer.add(bg);
var image = new Image();
image.onload = function() {
bg.image(image);
layer.draw();
};
image.src = 'data:image/svg+xml;base64,...';
function getDistance(p1, p2) {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
function getCenter(p1, p2) {
return {
x: (p1.x + p2.x) / 2,
y: (p1.y + p2.y) / 2,
};
}
var lastCenter = null;
var lastDist = 0;
stage.on('touchmove', function (e) {
e.evt.preventDefault();
var touch1 = e.evt.touches[0];
var touch2 = e.evt.touches[1];
if (touch1 && touch2) {
// if the stage was under Konva's drag&drop
// we need to stop it, and implement our own pan logic with two pointers
if (stage.isDragging()) {
stage.stopDrag();
}
var p1 = {
x: touch1.clientX,
y: touch1.clientY,
};
var p2 = {
x: touch2.clientX,
y: touch2.clientY,
};
if (!lastCenter) {
lastCenter = getCenter(p1, p2);
return;
}
var newCenter = getCenter(p1, p2);
var dist = getDistance(p1, p2);
if (!lastDist) {
lastDist = dist;
}
// local coordinates of center point
var pointTo = {
x: (newCenter.x - stage.x()) / stage.scaleX(),
y: (newCenter.y - stage.y()) / stage.scaleX(),
};
var scale = stage.scaleX() * (dist / lastDist);
stage.scaleX(scale);
stage.scaleY(scale);
// calculate new position of the stage
var dx = newCenter.x - lastCenter.x;
var dy = newCenter.y - lastCenter.y;
var newPos = {
x: newCenter.x - pointTo.x * scale + dx,
y: newCenter.y - pointTo.y * scale + dy,
};
stage.position(newPos);
lastDist = dist;
lastCenter = newCenter;
}
});
stage.on('touchend', function () {
lastDist = 0;
lastCenter = null;
});
layer.add(triangle);
layer.add(circle);
stage.add(layer);
I am unsure if this is due to the large size of the image and / or canvas or an inherent flaw in the example code from Konvas.js. This has been tested, with the same results, on 2 models of iPad Pro, iPhone X & 11, Android Pixel 3, 5 and 6 Pro.
Here is the code on codepen as an example: https://codepen.io/mr-jose/pen/WNXgbdG
Any help would be appreciated, thanks!