Drag while in background in KineticJs - javascript

I have two layers generally representing a large background and some content on top of it.
I have made the background draggable but when dragged, it covers the content of the other layer.
Is it possible to keep it in the background while dragging it?
I have tried binding the drag events with .moveToBottom() for the background group (later added to backgroundLayer) like that:
groupBackground.on('dragstart',function(){
groupBackground.moveToBottom();
});
groupBackground.on('dragmove',function(){
groupBackground.moveToBottom();
});
groupBackground.on('dragend',function(){
groupBackground.moveToBottom();
});
but to no result.

You can “draw under” by using the canvas “.globalCompositeOperation”
This will allow your user to drag the background under the foreground.
And you can apply it to KineticJs like this:
layer.getContext().globalCompositeOperation = "destination-over";
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/2GFSE/
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
stage.add(layer);
var background = new Kinetic.Rect({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
width: 250,
height: 250,
fill: 'black',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
var foreground = new Kinetic.Ellipse({
x: stage.getWidth() / 3,
y: stage.getHeight() / 3,
radius: {
x: 50,
y: 30
},
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
// add the background and foreground to the layer
layer.add(foreground);
layer.draw();
layer.getContext().globalCompositeOperation = "destination-over";
layer.add(background);
layer.draw();

Related

Making a kineticjs layer without a background

I currently have some javascript that receives an image from a variable and loads it into the canvas. The canvas is inside a div in order to use kineticjs. I'm loading a regular hexagon with the following code:
function MakeShape()
{
var stage = new Kinetic.Stage({
container: 'container',
width: 490,
height: 225
});
var polyLayer = new Kinetic.Layer();
var hexagon = new Kinetic.RegularPolygon({
x: stage.width()/2,
y: stage.height()/2,
sides: 6,
radius: 70,
fill: 'red',
offset: {
x: 100,
y: 0
},
draggable: true
});
polyLayer.add(hexagon);
stage.add(polyLayer);
}
However, when loading, the layer receives the background of the canvas, when I want the shape to be above the image. Do I have to draw the image onto the layer as well as the shape? How am I supposed to do this when the image is loaded before the shape? Thanks.
I'm not sure what you mean by "javascript receives an image from a variable and loads it into the Canvas". I'm guessing that your Canvas element's is assigned a background-image==that image.
Anyway, new Kinetic.Stage plus new Kinetic.Layer will create a new canvas that covers the container element. So any image you put in your Div and Canvas will be overlaid by the new Canvas that KineticJS creates.
Bottom line...Yes, draw the image onto the layer (using new Kinetic.Image) and eliminate your Canvas element with the image.
Good luck with your project!
[ Added Example code ]
var stage = new Kinetic.Stage({
container: 'container',
width: 490,
height: 225
});
var polyLayer = new Kinetic.Layer();
stage.add(polyLayer);
var hexagon,image1;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/nightscape.jpg";
function start(){
// add the image first
// It's z-index will be lower then the hexagon's z-index
// so the hexagon will be drawn over the image
image1=new Kinetic.Image({
x:0,
y:0,
image:img,
});
polyLayer.add(image1);
hexagon = new Kinetic.RegularPolygon({
x: stage.width()/2,
y: stage.height()/2,
sides: 6,
radius: 70,
fill: 'red',
offset: {
x: 100,
y: 0
},
draggable: true
});
polyLayer.add(hexagon);
polyLayer.draw();
}
body{padding:20px;}
#container{
border:solid 1px #ccc;
margin-top: 10px;
width:490px;
height:225px;
}
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.1.0.min.js"></script>
<h4>Background image with draggable hex on top.</h4>
<div id="container"></div>

How to pass an event on to the nodes below in kinetics

question that is very similar but answer doesn't work in my situation
I have a circle and I want to pass the click event to any objects that are underneath it.
I have tried:
setting cancelbubble to false.
setting the cirle layer to listening= false doesn't work for me because the circle is then no longer draggable.
I have made a fiddle here
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 400
});
var layer = new Kinetic.Layer();
var background = new Kinetic.Layer();
var colorPentagon = new Kinetic.RegularPolygon({
x: 80,
y: stage.getHeight() / 2,
sides: 5,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
colorPentagon.on('click', function(evt){ alert("pentagon");});
var linearGradPentagon = new Kinetic.RegularPolygon({
x: 360,
y: stage.height()/2,
sides: 5,
radius: 70,
fillLinearGradientStartPoint: {x:-50, y:-50},
fillLinearGradientEndPoint: {x:50,y:50},
fillLinearGradientColorStops: [0, 'red', 1, 'yellow'],
stroke: 'black',
strokeWidth: 4,
draggable: true
});
linearGradPentagon.on('click', function(evt){ alert("pentagon");});
var radialGradPentagon = new Kinetic.RegularPolygon({
x: 500,
y: stage.height()/2,
sides: 5,
radius: 70,
fillRadialGradientEndRadius: 70,
fillRadialGradientColorStops: [0, 'red', 0.5, 'yellow', 1, 'blue'],
stroke: 'black',
strokeWidth: 4,
draggable: true
});
radialGradPentagon.on('click', function(evt){ alert("pentagon");});
background.add(colorPentagon);
//background.add(patternPentagon);
background.add(linearGradPentagon);
background.add(radialGradPentagon);
stage.add(background);
var hideCircle = new Kinetic.Circle({
x: stage.width()/2,
y: stage.height()/2,
radius: 650,
stroke: 'black',
strokeWidth: 1000,
draggable:true
});
hideCircle.on('click', function(evt){ alert("click");});
// add the shape to the layer
layer.add(hideCircle);
// add the layer to the stage
stage.add(layer);
Currently to get it to work I have to take the click coordinates and do my own detection. It works but I was hoping for something more elegant.
Here's how to pass your click event to the bottom layer nodes:
You can listen for clicks on the stage
Determine if the click is inside any node on the bottom layer
If the click was inside a node, fire the click event on that node.
Here's example code and a Demo: http://jsfiddle.net/m1erickson/sFX8y/
stage.on('contentClick',function(e){
// get the mouse position
var pos=stage.getPointerPosition();
// fetch the node on the bottom layer that is under the mouse, if any.
var hitThisNode=background.getIntersection(pos);
// if a node was hit, fire the click event on that node
if(hitThisNode){
hitThisNode.fire("click",e,true);
}
});

KineticJS transform layers for scrolling

I've been trying to put scrollbars into my KineticJS app following the tutorial Kinetic have on the API. I have the scrollbars themselves appearing as they should, but I'm not sure what to do with the event listeners to actually get the stage or each of the layers to move so that the scrollbars actually move the view along.
var hscrollArea = new Kinetic.Rect({
x: 10,
y: $(window).height() - 30 - 80, // 80 to account for the fixed footer
width: $(window).width() - 40,
height: 20,
fill: "gray",
opacity: 0.3
});
var hscroll = new Kinetic.Rect({
x: 10,
y: $(window).height() - 30 - 80,// 80 to account for the fixed footer
width: 130,
height: 20,
fill: "orange",
draggable: true,
dragBoundFunc: function(pos) {
// TODO: Move stage or layers at this point
console.log("dragBoundFunc: " + this);
return {
x: pos.x,
y: this.getAbsolutePostion().y
};
},
opacity: 0.9,
stroke: "black",
strokeWidth: 1
});
var vscrollArea = new Kinetic.Rect({
x: $(window).width() - 30,
y: 10,
width: 20,
height: $(window).height() - 40 - 80,
fill: "gray",
opacity: 0.3
});
var vscroll = new Kinetic.Rect({
x: $(window).width() - 30,
y: 10,
width: 20,
height: 70,
fill: "orange",
draggable: true,
dragBoundFunc: function(pos) {
// TODO: Move stage or layers at this point
console.log("dragBoundFunc: " + this);
return {
x: this.getAbsolutePosition().x,
y: pos.y
};
},
opacity: 0.9,
stroke: "black",
strokeWidth: 1
});
Any help would be greatly appreciated :)
Thanks,
Becky
You can move stage or layer when your scrollbar(rectangle) is dragged. i.e,
stage.move(50,50);
stage.draw();
stage.move(-50,-50);
stage.draw();
I would recommend placing the objects you wish to scroll into their own group and position the group accordingly, rather than trying to position layers or the stage. The size of the group would be the (axis-aligned) bounding box of all the objects within the group. You can use the size of the group and compare it against the size of the stage to get a ratio for width and height. These ratios would be used to help calculate sizes for horizontal and vertical scroll bars (the bars being what get dragged to create a scroll effect). The ratio would also be used to determine when to show and hide the scroll bar areas. The difference in sizes would help in determining how to position the group within the stage.

KineticJS / Javascript dynamic creation and immediate dragging

What I want do is dynamically create a shape on mousedown, and then immediately have it (the shape) follow the mouse cursor until mouseup sets it in place.
Here is what I have so far and it's not working for me.
addNegativeButton.on('mousedown', function(){
var userPos = stage.getUserPosition();
shapesLayer.add(new Kinetic.Image({
image: imageObj,
x: userPos.x,
y: userPos.y,
height: 25,
width: 25,
rotation: 1,
draggable: true,
offset: 12
}));
var last = shapesLayer.getChildren()[shapesLayer.getChildren().length -1];
stage.on("mouseup", function(){
last.setAbsolutePosition(stage.getUserPosition());
stage.off("mouseup");
});
To reiterate:
What I have is an 'addNegativeButton' which when clicked creates a shape.
But I want it to follow the mouse cursor until the click is released.
http://jsfiddle.net/CPrEe/37/
Any suggestions?
Turns out its rather simple ;)
After you add the element/shape, all you have to do is use the simulate function to simulate mouse down and it drags....
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x: 20,
y: 20,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4
});
//What I really want here is to start dragging the circle until the
//user releases the mouse, which would then place the circle.
rect.on('mousedown', function(evt) {
var userPos = stage.getUserPosition();
var latestElement = new Kinetic.Circle({
x: userPos.x,
y: userPos.y,
radius: 20,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
})
layer.add(latestElement);
// Here's the bit you want. After adding the circle (with draggable:true) simulate the mousedown event on it which will initiate the drag
latestElement.simulate('mousedown');
layer.draw();
});
layer.add(rect);
stage.add(layer);​
http://jsfiddle.net/CPrEe/38/
http://kineticjs.com/docs/symbols/Kinetic.Node.php#simulate

Reset to Default position with KineticJS

I have the following circles in my game. The player drags them into different locations in the game. When the player selects, play again, I want the shapes to go back to the original position stated in the init function. How should I go about this? Thank you.
stage = new Kinetic.Stage({
container: "container",
width: 900,
height: 550
});
shapes = new Kinetic.Layer();
function init() {
circle1 = new Kinetic.Circle({
x: stage.getWidth() / 3.2,
y: stage.getHeight() / 3.2,
radius: radius,
fill: "blue",
stroke: "black",
strokeWidth: 4,
name: "circle",
draggable: true
});
shapes.add(circle1)
stage.add(shapes)
Store defaults in a separate variable, then use .setPosition( x, y ) to reset it:
//store settings
var settings = {
x: stage.getWidth() / 3.2,
y: stage.getHeight() / 3.2,
radius: radius,
fill: "blue",
stroke: "black",
strokeWidth: 4,
name: "circle",
draggable: true
};
//use settings
circle1 = new Kinetic.Circle(settings);
//after move, reset using:
circle1.setPosition(settings.x,settings.y);
you can also use the super handy setAttrs method like this:
circle1.setAttrs(settings);
Cheers!
I know this question is fairly old but I just solved something like this.
stage.removeChildren();
stage.setAttrs(stage.defaultNodeAttrs);
The stage.reset() doesn't work anymore, but this should do what you want.

Categories

Resources