The game I'm creating doesn't require any physics, however you are able to interact when hovering over/clicking on the sprite by using sprite.setInteractive({cursor: "pointer"});, sprite.on('pointermove', function(activePointer) {...}); and similar. However I noticed two issues with that:
The sprite has some area which are transparent. The interactive functions will still trigger when clicking on those transparent areas, which is unideal.
When playing a sprite animation, the interactive area doesn't seem to entirely (at all?) change, thus if the sprite ends on a frame bigger than the previous, there end up being small areas I can't interact with.
One option I thought of was to create a polygon over my sprite, which covers the area I want to be interactive. However before I do that, I simply wanted to ask if there are simpler ways to fix these issues.
Was trying to find an answer for this myself just now..
Think Make Pixel Perfect is what you're looking for.
this.add.sprite(x, y, key).setInteractive(this.input.makePixelPerfect());
https://newdocs.phaser.io/docs/3.54.0/focus/Phaser.Input.InputPlugin-makePixelPerfect
This might not be the best solution, but I would solve this problem like this. (If I don't want to use physics, and if it doesn't impact the performance too much)
I would check in the event-handler, if at the mouse-position the pixel is transparent or so, this is more exact and less work, than using bounding-boxes.
You would have to do some minor calculations, but it should work well.
btw.: if the origin is not 0, you would would have to compensate in the calculations for this. (in this example, the origin offset is implemented)
Here is a demo, for the click event:
let Scene = {
preload ()
{
this.load.spritesheet('brawler', 'https://labs.phaser.io/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
},
create ()
{
// Animation set
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('brawler', { frames: [ 0, 1, 2, 3 ] }),
frameRate: 8,
repeat: -1
});
// create sprite
const cody = this.add.sprite(200, 100).setOrigin(0);
cody.play('walk');
cody.setInteractive();
// just info text
this.mytext = this.add.text(10, 10, 'Click the Sprite, or close to it ...', { fontFamily: 'Arial' });
// event to watch
cody.on('pointerdown', function (pointer) {
// calculate x,y position of the sprite to check
let x = (pointer.x - cody.x) / (cody.displayWidth / cody.width)
let y = (pointer.y - cody.y) / (cody.displayHeight / cody.height);
// just checking if the properties are set
if(cody.anims && cody.anims.currentFrame){
let currentFrame = cody.anims.currentFrame;
let pixelColor = this.textures.getPixel(x, y, currentFrame.textureKey, currentFrame.textureFrame);
// alpha > 0 a visible pixel of the sprite, is clicked
if(pixelColor.a > 0) {
this.mytext.text = 'Hit';
} else {
this.mytext.text = 'No Hit';
}
// just reset the textmessage
setTimeout(_ => this.mytext.text = 'Click the Sprite, or close to it ...' , 1000);
}
}, this);
}
};
const config = {
type: Phaser.AUTO,
width: 400,
height: 200,
scene: Scene
};
const game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Related
'm working on an application which allows to make image processing, so I used Javascript and PixiJS library to make it possible. I wanted to update cursor image when canvas was hovered
first solution I tried to use cursor: url(cursor1.png) 4 12, auto; but I can't resize cursor. The default size is 64px and I can't set another value.
second solution I decided to add into DOM and update x,y position using Javascript but I got latency.
third solution was to integrate cursor inside my canvas.
last solution I tried to split actions into 2 canvas. The first deals with image processing and the second is my cursor.
With all propositions made before I got loss of FPS when canvas is hovered excepted the first one.
Init main canvas for image processing
function _initMainCanvas(imgData) {
let newCanvas = new PIXI.Application({
width: imgData.width,
height: imgData.height,
transparent: true
});
let blurContainer = new PIXI.Container();
filters.initFilters();
// ----------------------------------------------------------------------------------------
// Normal Sprite
// ----------------------------------------------------------------------------------------
let bg = main.createSprite({
from: imgData.img,
interactive: true,
filters: [filters.getFilterSharpen(), filters.getFilterAdjustment()],
width: imgData.width,
height: imgData.height
});
newCanvas.stage.addChild(bg);
//$(".blur_cursor").remove();
// ----------------------------------------------------------------------------------------
// Blur Sprite
// ----------------------------------------------------------------------------------------
let bgBlured = main.createSprite({
from: imgData.img,
interactive: false,
filters: filters.getFilters(),
width: imgData.width,
height: imgData.height
});
blurContainer.addChild(bgBlured);
blurContainer.mask = containers.getBlurs();
newCanvas.stage.addChild(blurContainer);
newCanvas.stage.addChild(blurContainer);
select.initSelectionRect();
newCanvas.stage.addChild(select.getSelectionRect());
canvas.addMainCanvas(newCanvas);
document.getElementById("container").appendChild(newCanvas.view);
}
Init canvas for cursor update when mouse hover it
function _initCursorCanvas(imgData) {
let cursorCanvas = new PIXI.Application({
width: imgData.width,
height: imgData.height,
transparent: true
});
_fillCursorCanvas(cursorCanvas);
canvas.addCursorCanvas(cursorCanvas);
document.getElementById("container").appendChild(cursorCanvas.view);
}
function _fillCursorCanvas(cursorCanvas) {
// emptySprite allows to bind events
let emptySprite = new PIXI.Sprite();
emptySprite.interactive = true;
emptySprite.width = cursorCanvas.screen.width;
emptySprite.height = cursorCanvas.screen.height;
cursorCanvas.stage.addChild(emptySprite);
emptySprite
.on("pointerdown", canvasEvents.handlerMousedown)
.on("pointerup", canvasEvents.handlerMouseup)
.on("pointermove", canvasEvents.handlerMousemove)
.on("pointerout", canvasEvents.handlerMouseout);
const scale = W / canvas.getWidth();
const cursorTexture = new PIXI.Texture.from(
urlManager.replace("index.php/", "") + "assets/images/cursor_img/50.png"
);
let circle = new PIXI.Sprite(cursorTexture);
circle.width = 50 / scale;
circle.height = 50 / scale;
cursorCanvas.stage.addChild(circle);
}
Mousemove event
const x = e.data.global.x;
const y = e.data.global.y;
cursor.updatePosition(x, y, W);
Will anyone know how to optimize FPS on mouse flying, thank you in advance !
Why do you need a second canvas just for that?
If you want to update the cursor do it at the end of the update loop and that's it, don't make a new canvas just for that.
I am VERY new to P5.js/processing (taking programming for artists). I am trying to make a crude game where an image (Jar Jar) bounces across the screen and another image (lightsaber) that moves with the mouse and when the mouse attached image goes over the bouncing image then the lightsaber will be mirrored and activate a sound. If this at all makes sense...
I have the bouncing image part down so far, but I am unable to make the mousePressed() function work. like I mentioned, I need the "lightsaber.png" to flip when the mouse is pressed. Also, when the mouse is pressed and is directly over the JarJar image, how would I add a score count and sound event?
Thank you!
here is my code so far:
let jarJar;
let jarJarX=5;
let jarJarY=5;
let xspeed;
let yspeed;
let lightSaber;
function preload() {
jarJar = loadImage('jarjar.png');
lightSaber= loadImage ('lightSaber.png');
}
function setup() {
createCanvas(700,700);
xspeed=random (15,22);
yspeed=random (15,22);
}
function draw() {
background(0);
image (lightSaber,mouseX,mouseY,100,100);
image(jarJar,jarJarX,jarJarY, 140, 200);
jarJarX= jarJarX+xspeed;
if (jarJarX<=-300|| jarJarX>=width+200){
xspeed=xspeed*-1;
}
jarJarY= jarJarY+yspeed;
if (jarJarY<-200|| jarJarY>=height+200 ){
yspeed=yspeed*-1;
}
//picture mirrors when mouse pressed
if mouseClicked(){
scale(-1,1);
image(lightSaber);
}
//score counter coordinate with lightsaber hitting image
//
}
Let it be known that I'm not proficient at javaScript. This said, your question is quite simple so I can help anyway.
Some framework will have simple ways to mirror images. Processing likes to scale with a negative number. I re-coded some of your stuff to accommodate my changes. The main changes goes as follows:
I added a method to draw the lightsaber so we can "animate" it (read: flip it for a couple frames when the user clicks around).
I added a 'score' global variable to track the score, and a way for the user to see that score with the text method.
I added a method called "intersect" which isn't very well coded as it's something I did back when I was a student (please don't hurt me, it works just right so I still use it from time to time). For more details on how simple collisions works, take some time to read this answer I wrote some time ago, there are nice pictures too!
I added a mouseClicked method. This method will act like an event, which means that it will be triggered by a specific call (a left mouse button click in this case). This method contains the code to check for a collision between the squares which are the images. If there's an overlap, the score will increase and jarjar will run in another direction (this part is a bonus to demonstrate that this is the place where you can get creative about the collision).
I commented the code so you can get what I'm doing more easily:
let jarJar;
let jarJarX=5;
let jarJarY=5;
let xspeed;
let yspeed;
let lightSaber;
let flipLength;
let score = 0;
function preload() {
jarJar = loadImage('jarjar.png');
lightSaber= loadImage ('lightSaber.png');
}
function setup() {
createCanvas(700, 700);
runJarJarRun();
}
function draw() {
background(0);
drawLightSaber(); // this way I can deal with the lightsaber's appearance in a dedicated method
image(jarJar, jarJarX, jarJarY, 140, 200);
jarJarX= jarJarX+xspeed;
if (jarJarX<=-300|| jarJarX>=width+200) {
xspeed=xspeed*-1;
}
jarJarY= jarJarY+yspeed;
if (jarJarY<-200|| jarJarY>=height+200 ) {
yspeed=yspeed*-1;
}
//score counter coordinate with lightsaber hitting image
textSize(30);
fill(200, 200, 0);
text('Score: ' + score, 10, 40);
}
function drawLightSaber() {
if (flipLength) { // if the number is > 0 this will be true
flipLength--; // measure how ling the saber is flipped in frames # ~60 frames per second
push(); // isolating the translate ans scale manpulations to avoid ruining the rest of the sketch
translate(mouseX + 100, 0); // makes the coordinates so once flipped the lightsaber will still appear at the same location
scale(-1.0, 1.0); // flip x-axis backwards
image (lightSaber, 0, mouseY, 100, 100);
pop(); // ends the sequence started with 'push();'
} else {
image (lightSaber, mouseX, mouseY, 100, 100);
}
}
function runJarJarRun() {
xspeed=random (5, 10);
yspeed=random (5, 10);
}
function mouseClicked() { // this method will trigger once when the left mouse button is clicked
flipLength = 10;
if (intersect(jarJarX, jarJarY, 140, 200, mouseX, mouseY, 100, 100)) {
score++;
runJarJarRun(); // as a bonus, jarjar will run in another direction on hit
// you could totally put some more special effects, like a flash, a sound, some 'mesa ouchie bad!' text, whatever speaks to you
}
}
function intersect(x1, y1, w1, h1, x2, y2, w2, h2) {
let checkX = false;
let checkY = false;
if ( (x1<x2 && (x1+w1)>x2) || (x1<(x2+w2) && (x1+w1)>x2+w2) || (x1>x2 && (x1+w1)<(x2+w2)) ) {
checkX = true;
}
if ( (y1<y2 && (y1+h1)>y2) || (y1<(y2+h2) && (y1+h1)>y2+h2) || (y1>y2 && (y1+h1)<(y2+h2)) ) {
checkY = true;
}
return (checkX && checkY);
}
If there's something you don't understand, let me know in a comment and I'll be happy to elaborate. Good luck and have fun!
Hi and welcome to stack overflow. One thing to keep in mind when submitting here (or any forum where you're looking for help with code) is to post a minimal reproducible example. You'll be much more likely to get useful responses.
You'll also want to separate out your questions, as they each have multi-step responses.
Your first question is about how to get your sketch to display something when you press the mouse down. Your syntax isn't quite correct there. Here's a minimal example of how to check for a mouse held down.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
if (mouseIsPressed == true) {
ellipse(100, 100, 100, 100);
}
}
Just a quick note that I tried to make this as 'novice-friendly' as possible. The == true is optional and not usually included.
I'm pretty much new to canvas. What I'm trying to make is that I can write text in canvas using input and can be able to resize it by dragging it's corners. Also I should be able to drag text position within the canvas.
Following is the screen shot of what I want!
Canvas is raster, not vector. By simply drawing and resizing text you would expect it to get blurry or pixelated. And redrawing the whole canvas each time user moves the cursor while resizing will not result in the best performance. Consider using svg instead. In case you do need canvas and don't want to implement all the functions yourself, you can use the paperjs library.
http://paperjs.org/reference/pointtext/
As #hirasawa-yui mentioned, you can use Paper.js to greatly facilitate the implementation of what you want in a canvas.
Here is a simplified sketch showing a possible implementation of dragging/resizing interactions.
// create item
var item = new PointText({
content: 'Custom text content',
point: view.center,
justification: 'center',
fontSize: 30,
selected: true
});
// init variables so they can be shared by event handlers
var resizeVector;
var moving;
// on mouse down...
function onMouseDown(event) {
// ...do a hit test on item bounds with a small tolerance for better UX
var cornerHit = item.hitTest(event.point, {
bounds: true,
tolerance: 5
});
// if a hit is detected on one of the corners...
if (cornerHit && ['top-left', 'top-right', 'bottom-left', 'bottom-right'].indexOf(cornerHit.name) >= 0) {
// ...store current vector from item center to point
resizeVector = event.point - item.bounds.center;
// ...else if hit is detected inside item...
} else if (item.hitTest(event.point, { fill: true })) {
// ...store moving state
moving = true;
}
}
// on mouse drag...
function onMouseDrag(event) {
// ...if a corner was previously hit...
if (resizeVector) {
// ...calculate new vector from item center to point
var newVector = event.point - item.bounds.center;
// scale item so current mouse position is corner position
item.scale(newVector / resizeVector);
// store vector for next event
resizeVector = newVector;
// ...if item fill was previously hit...
} else {
// ...move item
item.position += event.delta;
}
}
// on mouse up...
function onMouseUp(event) {
// ... reset state
resizeVector = null;
moving = null;
}
// draw instructions
new PointText({
content: 'Drag rectangle to move, drag corners to resize.',
point: view.center + [0, -50],
justification: 'center'
});
I'm working in a Paper.js project where we're essentially doing image editing. There is one large Raster. I'm attempting to use the getSubRaster method to copy a section of the image (raster) that the user can then move around.
After the raster to edit is loaded, selectArea is called to register these listeners:
var selectArea = function() {
if(paper.project != null) {
var startDragPoint;
paper.project.layers[0].on('mousedown', function(event) { // TODO should be layer 0 in long run? // Capture start of drag selection
if(event.event.ctrlKey && event.event.altKey) {
startDragPoint = new paper.Point(event.point.x + imageWidth/2, (event.point.y + imageHeight/2));
//topLeftPointOfSelectionRectangleCanvasCoordinates = new paper.Point(event.point.x, event.point.y);
}
});
paper.project.layers[0].on('mouseup', function(event) { // TODO should be layer 0 in long run? // Capture end of drag selection
if(event.event.ctrlKey && event.event.altKey) {
var endDragPoint = new paper.Point(event.point.x + imageWidth/2, event.point.y + imageHeight/2);
// Don't know which corner user started dragging from, aggregate the data we have into the leftmost and topmost points for constructing a rectangle
var leftmostX;
if(startDragPoint.x < endDragPoint.x) {
leftmostX = startDragPoint.x;
} else {
leftmostX = endDragPoint.x;
}
var width = Math.abs(startDragPoint.x - endDragPoint.x);
var topmostY;
if(startDragPoint.y < endDragPoint.y) {
topmostY = startDragPoint.y;
} else {
topmostY = endDragPoint.y;
}
var height = Math.abs(startDragPoint.y - endDragPoint.y);
var boundingRectangle = new paper.Rectangle(leftmostX, topmostY, width, height);
console.log(boundingRectangle);
console.log(paper.view.center);
var selectedArea = raster.getSubRaster(boundingRectangle);
var selectedAreaAsDataUrl = selectedArea.toDataURL();
var subImage = new Image(width, height);
subImage.src = selectedAreaAsDataUrl;
subImage.onload = function(event) {
var subRaster = new paper.Raster(subImage);
// Make movable
subRaster.onMouseEnter = movableEvents.showSelected;
subRaster.onMouseDrag = movableEvents.dragItem;
subRaster.onMouseLeave = movableEvents.hideSelected;
};
}
});
}
};
The methods are triggered at the right time and the selection box seems to be the right size. It does indeed render a new raster for me that I can move around, but the contents of the raster are not what I selected. They are close to what I selected but not what I selected. Selecting different areas does not seem to yield different results. The content of the generated subraster always seems to be down and to the right of the actual selection.
Note that as I build the points for the bounding selection rectangle I do some translations. This is because of differences in coordinate systems. The coordinate system where I've drawn the rectangle selection has (0,0) in the center of the image and x increases rightward and y increases downward. But for getSubRaster, we are required to provide the pixel coordinates, per the documentation, which start at (0,0) at the top left of the image and increase going rightward and downward. Consequently, as I build the points, I translate the points to the raster/pixel coordinates by adding imageWidth/2 and imageHeight/2`.
So why does this code select the wrong area? Thanks in advance.
EDIT:
Unfortunately I can't share the image I'm working with because it is sensitive company data. But here is some metadata:
Image Width: 4250 pixels
Image Height: 5500 pixels
Canvas Width: 591 pixels
Canvas Height: 766 pixels
My canvas size varies by the size of the browser window, but those are the parameters I've been testing in. I don't think the canvas dimensions are particularly relevant because I'm doing everything in terms of image pixels. When I capture the event.point.x and event.point.y to the best of my knowledge these are image scaled coordinates, but from a different origin - the center rather than the top left. Unfortunately I can't find any documentation on this. Observe how the coordinates work in this sketch.
I've also been working on a sketch to illustrate the problem of this question. To use it, hold Ctrl + Alt and drag a box on the image. This should trigger some logging data and attempt to get a subraster, but I get an operation insecure error, which I think is because of security settings in the image request header. Using the base 64 string instead of the URL doesn't give the security error, but doesn't do anything. Using that string in the sketch produces a super long URL I can't paste here. But to get that you can download the image (or any image) and convert it here, and put that as the img.src.
The problem is that the mouse events all return points relative to 0, 0 of the canvas. And getSubRaster expects the coordinates to be relative to the 0, 0 of the raster item it is extracting from.
The adjustment needs to be eventpoint - raster.bounds.topLeft. It doesn't really have anything to do with the image width or height. You want to adjust the event points so they are relative to 0, 0 of the raster, and 0, 0 is raster.bounds.topLeft.
When you adjust the event points by 1/2 the image size that causes event points to be offset incorrectly. For the Mona Lisa example, the raster size (image size) is w: 320, h: 491; divided by two they are w: 160, h: 245.5. But bounds.topLeft of the image (when I ran my sketch) was x: 252.5, y: 155.5.
Here's a sketch that shows it working. I've added a little red square highlighting the selected area just to make it easier to compare when it's done. I also didn't include the toDataURL logic as that creates the security issues you mentioned.
Here you go: Sketch
Here's code I put into an HTML file; I noticed that the sketch I put together links to a previous version of the code that doesn't completely work.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rasters</title>
<script src="./vendor/jquery-2.1.3.js"></script>
<script src="./vendor/paper-0.9.25.js"></script>
</head>
<body>
<main>
<h3>Raster Bug</h3>
<div>
<canvas id="canvas"></canvas>
</div>
<div id="position">
</div>
</main>
<script>
// initialization code
$(function() {
// setup paper
$("#canvas").attr({width: 600, height: 600});
var canvas = document.getElementById("canvas");
paper.setup(canvas);
// show a border to make range of canvas clear
var border = new paper.Path.Rectangle({
rectangle: {point: [0, 0], size: paper.view.size},
strokeColor: 'black',
strokeWidth: 2
});
var tool = new paper.Tool();
// setup mouse position tracking
tool.on('mousemove', function(e) {
$("#position").text("mouse: " + e.point);
});
// load the image from a dataURL to avoid CORS issues
var raster = new paper.Raster(dataURL);
raster.position = paper.view.center;
var lt = raster.bounds.topLeft;
var startDrag, endDrag;
console.log('rb', raster.bounds);
console.log('lt', lt);
// setup mouse handling
tool.on('mousedown', function(e) {
startDrag = new paper.Point(e.point);
console.log('sd', startDrag);
});
tool.on('mousedrag', function(e) {
var show = new paper.Path.Rectangle({
from: startDrag,
to: e.point,
strokeColor: 'red',
strokeWidth: 1
});
show.removeOn({
drag: true,
up: true
});
});
tool.on('mouseup', function(e) {
endDrag = new paper.Point(e.point);
console.log('ed', endDrag);
var bounds = new paper.Rectangle({
from: startDrag.subtract(lt),
to: endDrag.subtract(lt)
});
console.log('bounds', bounds);
var sub = raster.getSubRaster(bounds);
sub.bringToFront();
var subData = sub.toDataURL();
sub.remove();
var subRaster = new paper.Raster(subData);
subRaster.position = paper.view.center;
});
});
var dataURL = ; // insert data or real URL here
</script>
</body>
</html>
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!
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/
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.
this is what i have done so far.. hope it will help you.
http://jsfiddle.net/v1r00z/ZJE7w/
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.
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.
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/
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/