Zoom and Pan in KineticJS
Asked Answered
A

7

9

Is there a way one could zoom and pan on a canvas using KineticJS? I found this library kineticjs-viewport, but just wondering if there is any other way of achieving this because this library seems to be using so many extra libraries and am not sure which ones are absolutely necessary to get the job done.

Alternatively, I am even open to the idea of drawing a rectangle around the region of interest and zooming into that one particular area. Any ideas on how to achieve this? A JSFiddle example would be awesome!

Acheron answered 28/9, 2012 at 4:24 Comment(0)
P
25

You can simply add .setDraggable("draggable") to a layer and you will be able to drag it as long as there is an object under the cursor. You could add a large, transparent rect to make everything draggable. The zoom can be achieved by setting the scale of the layer. In this example I'm controlling it though the mousewheel, but it's simply a function where you pass the amount you want to zoom (positive to zoom in, negative to zoom out). Here is the code:

var stage = new Kinetic.Stage({
    container: "canvas",
    width: 500,
    height: 500
});

var draggableLayer = new Kinetic.Layer();
draggableLayer.setDraggable("draggable");

//a large transparent background to make everything draggable
var background = new Kinetic.Rect({
    x: -1000,
    y: -1000,
    width: 2000,
    height: 2000,
    fill: "#000000",
    opacity: 0
});

draggableLayer.add(background);


//don't mind this, just to create fake elements
var addCircle = function(x, y, r){
  draggableLayer.add(new Kinetic.Circle({
        x: x*700,
        y: y*700,
        radius: r*20,
        fill: "rgb("+ parseInt(255*r) +",0,0)"
    })
  );
}

var circles = 300
while (circles) {
  addCircle(Math.random(),Math.random(), Math.random())
  circles--;
}

var zoom = function(e) {
  var zoomAmount = e.wheelDeltaY*0.001;
  draggableLayer.setScale(draggableLayer.getScale().x+zoomAmount)
  draggableLayer.draw();
}

document.addEventListener("mousewheel", zoom, false)

stage.add(draggableLayer)

http://jsfiddle.net/zAUYd/

Payer answered 6/10, 2012 at 10:19 Comment(6)
This works great except in IE9. Would you have any idea as to why? The debug console doesn't throw any errors either...Acheron
Looks like it works if I use e.wheelDelta instead of e.wheelDeltaY. Any idea why?Acheron
I think IE uses deltaY. The mousewheel zoom was just an example, if you intend to keep it then you should use a shim that makes up for the difference in browser implementations such as github.com/cobbweb/jquery-mousewheelPayer
Awesome! Thank you once again for your help.Acheron
nice, one problem is if the layer is draggable, the objects on that layer are not draggale although it is set to be draggable. Do you know any way to set only background is draggable for panning?Cliff
The jsfiddle : jsfiddle.net/zAUYd doesn't work anymore, neither on FF, nor Chrome. Could you update it ?Sanity
U
4

Here's a very quick and simple implementation of zooming and panning a layer. If you had more layers which would need to pan and zoom at the same time, I would suggest grouping them and then applying the on("click")s to that group to get the same effect.

http://jsfiddle.net/renyn/56/

If it's not obvious, the light blue squares in the top left are clicked to zoom in and out, and the pink squares in the bottom left are clicked to pan left and right.

Edit: As a note, this could of course be changed to support "mousedown" or other events, and I don't see why the transformations couldn't be implemented as Kinetic.Animations to make them smoother.

Understood answered 4/10, 2012 at 12:38 Comment(4)
Awesome! This is exactly what I want. I tried using the mouse events over a stage but did not have much luck with it so I am tempted to ask you - would you be able to guide me more on supporting the mousedown events and supporting a smoother animation?Acheron
I wouldn't advise applying them to the whole stage, this would capture all mouse events without prejudice. Try assigning them to individual shapes like I did. I'm not sure exactly what guidance you need, the APIs and tutorials for Kinetic are pretty helpful though.Understood
I'm not surprised, 5.0.0 and 5.0.1 have plenty of breaking changes from 4.0.2.Understood
Could you update the jsfiddle mentionned in your answer ? It doesn't work anymore (Firefox, Chrome)Sanity
P
3

this is what i have done so far.. hope it will help you.

http://jsfiddle.net/v1r00z/ZJE7w/

Phenyl answered 2/10, 2012 at 2:19 Comment(0)
B
3

I actually wrote kineticjs-viewport. I'm happy to hear you were interested in it.

It is actually intended for more than merely dragging. It also allows zooming and performance-focused clipping. The things outside of the clip region aren't rendered at all, so you can have great rendering performance even if you have an enormous layer with a ton of objects.

That's the use case I had. For example, a large RTS map which you view via a smaller viewport region -- think Starcraft.

I hope this helps.

Barring answered 2/4, 2013 at 2:25 Comment(2)
Can you add an example on how to use the viewport for this?Leda
There's an demo page in the repo: kineticjs-viewport.googlecode.com/git/demo.htmlBarring
I
2

As I was working with Kinetic today I found a SO question that might interest you.

I know it would be better as a comment, but I don't have enough rep for that, anyway, I hope that helps.

Isoniazid answered 1/10, 2012 at 20:54 Comment(0)
C
2

These answers seems not to work with the KineticJS 5.1.0. These do not work mainly for the signature change of the scale function:

 stage.setScale(newscale); --> stage.setScale({x:newscale,y:newscale});

However, the following solution seems to work with the KineticJS 5.1.0:

JSFiddle: http://jsfiddle.net/rpaul/ckwu7u86/3/

Cuneo answered 12/11, 2014 at 5:53 Comment(0)
C
1

Unfortunately, setting state or layer draggable prevents objects not draggable. Duopixel's zooming solution is good, but I would rather set it for stage level, not layer level.

Her is my solution

var stage = new Kinetic.Stage({
    container : 'container',
    width: $("#container").width(),
    height: $("#container").height(),
});
var layer = new Kinetic.Layer();

//layer.setDraggable("draggable");
var center = { x:stage.getWidth() / 2, y: stage.getHeight() / 2};

var circle = new Kinetic.Circle({
    x: center.x-100,
    y: center.y,
    radius: 50,
    fill: 'green',
    draggable: true
});
layer.add(circle);
layer.add(circle.clone({x: center.x+100}));

// zoom by scrollong
document.getElementById("container").addEventListener("mousewheel", function(e) {
  var zoomAmount = e.wheelDeltaY*0.0001;
  stage.setScale(stage.getScale().x+zoomAmount)
  stage.draw();
  e.preventDefault();
}, false)

// pan by mouse dragging on stage
stage.on("dragstart dragmove", function(e) {window.draggingNode = true;});
stage.on("dragend", function(e) { window.draggingNode = false;});
$("#container").on("mousedown", function(e) {
    if (window.draggingNode) return false;
    if (e.which==1) {
        window.draggingStart = {x: e.pageX, y: e.pageY, stageX: stage.getX(), stageY: stage.getY()};
        window.draggingStage = true;
    }
});
$("#container").on("mousemove", function(e) {
    if (window.draggingNode || !window.draggingStage) return false;
    stage.setX(window.draggingStart.stageX+(e.pageX-window.draggingStart.x));
    stage.setY(window.draggingStart.stageY+(e.pageY-window.draggingStart.y));
    stage.draw();
});
$("#container").on("mouseup", function(e) { window.draggingStage = false } );

stage.add(layer);

http://jsfiddle.net/bighostkim/jsqJ2/

Cliff answered 20/12, 2012 at 21:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.