So my problem seems to be that a draggable object always is drawn over other objects.
Check out my fiddle.
and my code:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
canvas {
border: 1px solid #9C9898;
}
</style>
<script src="http://www.html5canvastutorials.com/libraries/kinetic-v4.3.1-beta.js"></script>
<script>
window.onload = function() {
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var cableLayer = new Kinetic.Layer();
// build cable
var cable = new Kinetic.Line({
strokeWidth: 40,
stroke: 'green',
points: [{
x: stage.getWidth() / 2,
y: stage.getHeight() / 2
}, {
x: 100,
y: 100
}],
draggable: true
});
// build center
var c1 = new Kinetic.Circle({
radius: 60,
fill: 'black',
draggable: true,
x: stage.getWidth() / 2,
y: stage.getHeight() / 2
});
var c2 = new Kinetic.Circle({
x: 100,
y: 100,
radius: 60,
fill: 'black',
draggable: true,
});
//add everything to the layer
cableLayer.add(cable);
cableLayer.add(c1);
cableLayer.add(c2);
//add all to stage
stage.add(cableLayer);
//What to do when something is changed...
cable.on('dragmove', (function () {
c1.setPosition([cable.getPosition().x + cable.getPoints()[0].x, cable.getPosition().y + cable.getPoints()[0].y]);
c2.setPosition([cable.getPosition().x + cable.getPoints()[1].x, cable.getPosition().y + cable.getPoints()[1].y]);
cableLayer.draw();
}));
c1.on('dragstart', (function () {
c1.getLayer().afterDraw(function () {
cable.attrs.points[0].x = c1.getX()-cable.getX();
cable.attrs.points[0].y = c1.getY()-cable.getY();
cableLayer.draw();
});
}));
c2.on('dragstart', (function () {
c2.getLayer().afterDraw(function () {
cable.attrs.points[1].x = c2.getX()-cable.getX();
cable.attrs.points[1].y = c2.getY()-cable.getY();
cableLayer.draw();
});
}));
}
</script>
</head>
<body onmousedown="return false;">
<div id="container"></div>
</body>
</html>
So, I tried to set the index using
cable.on('dragmove', (function () {
c1.setPosition([cable.getPosition().x + cable.getPoints()[0].x, cable.getPosition().y + cable.getPoints()[0].y]);
c2.setPosition([cable.getPosition().x + cable.getPoints()[1].x, cable.getPosition().y + cable.getPoints()[1].y]);
c2.setIndex(1);
c1.setIndex(1);
cable.setIndex(2);
cableLayer.draw();
}));
Doesn't seem to work? Why is this? How do I get the circles to draw over the line in all cases? Mainly the case that you drag the line.
Is there somewhere I'm setting this differently somewhere else?
Thanks for your help
http://jsfiddle.net/nYHrg/3/
So the problem is that kineticjs (4.3.0) added a new 'drag' layer to improve dragging performance. Any time that you drag an object, it is put in the drag layer, and when you stop dragging, it is put back in it's own layer. This does not preserve your original z-index. Anytime you create a new object and you want to override this functionality you will have to set one of the attributes of the object as:
"dragOnTop: false"
see jsfiddle for exact implementation.
Related
I'm looking for a way to obtain ID (123 in the code below) of a Konva Label ("Hello World!" in the code below) from Konva's 'dblclick' event.
I could obtain ID (321 in the code below) of a Konva Image (Konva's LOGO "K" in the code below) from both of Konva's 'dblclick' and 'dragmove' events.
Also I could obtain the ID (123 in the code below) of the Konva Label ("Hello World!") from 'dragmove' event; however I couldn't obtain the ID (123 in the code below) of the Konva Label ("Hello World!") from Konva's 'dblclick' event.
My code:
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva#5.0.2/konva.min.js"></script>
<meta charset="utf-8" />
</head>
<body>
<span id="container"></span>
</body>
<script>
//Konva Stage
var stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
//Konva Layer
var layer = new Konva.Layer();
stage.add(layer);
//Konva Image
var imageObj = new Image();
imageObj.src = 'https://raw.githubusercontent.com/konvajs/konvajs.github.io/master/apple-touch-icon-180x180.png';
imageObj.addEventListener('load', function() {
var kImage = new Konva.Image({
x: window.innerWidth*2.5/10,
y: window.innerHeight*1/10,
image: imageObj,
id: 321,
draggable: true,
});
layer.add(kImage);
layer.draw();
});
//Konva Label
WordLabel = new Konva.Label({
x: window.innerWidth*3/10,
y: window.innerHeight*4/10,
opacity: 0.75,
draggable: true,
fill: 'green',
id: 123,
});
//Konva Tag
WordLabel.add(
new Konva.Tag({
fill: 'green',
})
);
//Konva Text
WordLabel.add(
new Konva.Text({
text: 'Hello World!',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'white',
strokeWidth: 0,
})
);
layer.add(WordLabel);
layer.draw();
//Konva dragstart event
stage.on('dragstart', function(e) {
console.log('ID (dragstart) = ' + parseInt(e.target.id()));
});
//Konva dblclick event
stage.on('dblclick dbltap', function (e) {
console.log('ID (dblclick) = ' +parseInt(e.target.id()));
});
</script>
</html>
If you look at the e.target object you can see that it is the text node rather than the label node that received the click.
Use e.target.findAncestors (see shape API) to get and iterate the shape hierarchy looking for an ancestor shape with the ID attribute you seek.
Working snippet below.
//Konva Stage
var stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
//Konva Layer
var layer = new Konva.Layer();
stage.add(layer);
//Konva Image
var imageObj = new Image();
imageObj.src = 'https://raw.githubusercontent.com/konvajs/konvajs.github.io/master/apple-touch-icon-180x180.png';
imageObj.addEventListener('load', function() {
var kImage = new Konva.Image({
x: window.innerWidth*2.5/10,
y: window.innerHeight*1/10,
image: imageObj,
id: 321,
draggable: true,
});
layer.add(kImage);
layer.draw();
});
//Konva Label
WordLabel = new Konva.Label({
x: 10,
y: 20,
opacity: 0.75,
draggable: true,
fill: 'green',
id: 123,
});
//Konva Tag
WordLabel.add(
new Konva.Tag({
fill: 'green'
})
);
//Konva Text
WordLabel.add(
new Konva.Text({
text: 'Hello World!',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'white',
strokeWidth: 0
})
);
layer.add(WordLabel);
layer.draw();
//Konva dragstart event
stage.on('dragstart', function(e) {
console.log('ID (dragstart) = ' + parseInt(e.target.id()));
});
//Konva dblclick event
stage.on('dblclick dbltap', function (e) {
// Looking for a specif attr on a parent shape. In this
// case we know the parent is a Label so search for ancestors of that type
var nodes = e.target.findAncestors('Label', true);
if (nodes.length > 0) {
for (var i = 0; i < nodes.length; i++){
var id = nodes[i].getAttr("id")
console.log('shape ' + i + ' ID (dblclick)', id )
}
}
else {
console.log('ID (dblclick) = ' + parseInt(e.target.id()));
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva#5.0.2/konva.min.js"></script>
<meta charset="utf-8" />
</head>
<body>
<span id="container"></span>
</body>
<script>
</script>
</html>
I am using Kinetic for some image processing. What happens is that I crop my image and then by clicking a button I want to make it black and white. For some reason the simple setFilter function is not working in this case, when you do crop first.
This is the code for cropping:
layer.removeChildren();
layer.clear();
image = new Kinetic.Image({
image: canvasImage,
x: (canvasWidth/2-theSelection.w/2),
y: (canvasHeight/2-theSelection.h/2),
width: theSelection.w,
height: theSelection.h,
crop: [theSelection.x, theSelection.y, theSelection.w, theSelection.h],
name: "image_tmp"
});
layer.add(image);
stage.draw();
And here is the function I decided to use for applying the filter:
var imgPixels = ctx.getImageData(xx, yy, imgW, imgH);
for(var y = 0; y < imgPixels.height; y++){
for(var x = 0; x < imgPixels.width; x++){
var i = (y * 4) * imgPixels.width + x * 4;
var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
imgPixels.data[i] = avg;
imgPixels.data[i + 1] = avg;
imgPixels.data[i + 2] = avg;
}
}
ctx.putImageData(imgPixels, xx, yy, 0, 0, imgPixels.width, imgPixels.height);
So now I get my cropped image with the filter, but if I want to continue doing something to the image object, I am getting:
TypeError: a.getType is not a function
I think also that the image object I used to use in my code, is now like undefined.
So for example I want after the filter to do layer.add(image) and I want image variable to be the new black and white one and not the old one.
So does anyone have an idea what is the problem, or how can I make the new imgPixels to be the same as my image. Thanks in advance
Is there any reason you didn't use the Kinetic.Filters.Grayscale filter?
Here are 2 ways you can do it:
1) Use setFilter (it works!)
var imageObj = new Image();
imageObj.onload = function() {
var yoda = new Kinetic.Image({
x: 200,
y: 50,
image: imageObj,
crop: [0, 0, 50, 100]
});
// add the shape to the layer
layer.add(yoda);
// add the layer to the stage
stage.add(layer);
yoda.setFilter(Kinetic.Filters.Grayscale);
layer.draw();
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg';
2) Set the filter property on the image beforehand
var imageObj = new Image();
imageObj.onload = function() {
var yoda = new Kinetic.Image({
x: 200,
y: 50,
image: imageObj,
crop: [0, 0, 50, 100],
filter: Kinetic.Filters.Grayscale
});
// add the shape to the layer
layer.add(yoda);
// add the layer to the stage
stage.add(layer);
//yoda.setFilter(Kinetic.Filters.Grayscale);
//layer.draw();
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg';
Either way you don't need to add any new images, the original Kinetic.Image is filtered black & white.
UPDATE
Open this link: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-image-tutorial/
And copy and paste this code, replacing all the code in the link. It's working fine for me..
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<button id="crop">Crop</button>
<button id="gray">Grayscale</button>
<button id="both">Both</button>
<div id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.7.0.min.js"></script>
<script defer="defer">
var yoda;
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function() {
yoda = new Kinetic.Image({
x: 200,
y: 50,
image: imageObj,
width: 106,
height: 118
});
// add the shape to the layer
layer.add(yoda);
// add the layer to the stage
stage.add(layer);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg';
document.getElementById('crop').addEventListener('click', function() {
yoda.setCrop([20, 20, 50, 50]);
layer.draw();
});
document.getElementById('gray').addEventListener('click', function() {
yoda.setFilter(Kinetic.Filters.Grayscale);
layer.draw();
});
document.getElementById('both').addEventListener('click', function() {
yoda.setCrop([20, 20, 50, 50]);
yoda.setFilter(Kinetic.Filters.Grayscale);
layer.draw();
});
</script>
</body>
</html>
I am using KineticJs to create shapes with some text label(draggable along with the shape). There wasn't any info on the tutorial. Neither did I find this a very clean approach. What's a good approach to do so? The code below only creates the shape.
HTML:
<html>
<body>
<div id="container"> </div>
<button id="new_state">New State</button>
</body>
</html>
JS:
$(document).bind("ready", function () {
stage = new Kinetic.Stage({
container: 'container',
width: 600,
height: 500
});
layer = new Kinetic.Layer();
$('#new_state').click(function() {
newState();
});
});
newState = function() {
var circle = new Kinetic.Circle({
x: stage.getWidth()/2,
y: stage.getHeight()/2,
radius: 20,
fill: 'white',
stroke: 'black',
strokeWidth: 2,
text: 'tet',
draggable: true
});
circle.on('mouseover', function() {
$('body').css('cursor', 'pointer');
});
circle.on('mouseout', function() {
$('body').css('cursor', 'default');
});
layer.add(circle);
stage.add(layer);
};
JsFiddle here
You just need to add both the circle and text to a group and make the group draggable.
When grouped, the objects act as one item.
var group = new Kinetic.Group({
draggable: true
});
group.add(circle);
group.add(text);
then add the group to the layer
layer.add(group);
http://jsfiddle.net/e8KwC/1/
I have a grid and I'm looking to basically add a tooltip Image for each rectangle in the grid. Basically, first I need to be able to add an image to the canvas on the rectangle mouse over event. Eventually each rectangle would have it's own image so I need to keep track of the rectangles...do I add them to an array?
Here is my fiddle for what I've got so far:
http://jsfiddle.net/marseilles84/7ZzTh/1/
Here is a sample image source to use:
'http://www.html5canvastutorials.com/demos/assets/yoda.jpg';
<div id="container"></div>
var stage = new Kinetic.Stage({
container: 'container',
width: 1000,
height: 500
});
var layer = new Kinetic.Layer();
for (var i=0; i<7; i++)
{
for(c=0; c<18; c++)
{
var colorPentagon = new Kinetic.Rect({
x: (45*c),
y: 45*i,
width:40,
height:40,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
colorPentagon.on('mouseover touchstart', function() {
//code here
});
layer.add(colorPentagon);
}
}
stage.add(layer);
http://jsfiddle.net/7ZzTh/2/
This is probably more of what you're looking for.
colorPentagon.on('mouseover touchstart', function() {
var userPos = stage.getUserPosition();
yoda.setPosition(userPos.x,userPos.y);
layer.add(yoda);
layer.draw();
});
colorPentagon.on('mouseout touchstart', function() {
yoda.remove();
layer.draw();
});
At the beginning you want:
var imageObj = new Image();
var yoda = new Kinetic.Image({
x: 0,
y: 0,
image: imageObj,
width: 106,
height: 118
});
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg';
I have been playing about with kineticjs and the canvas as of the last couple of days. I have a drag and drop canvas that loads a resizable image. the anchors on the resizable image are circles:
var anchor;
function addAnchor(group, x, y, name) {
var stage = group.getStage();
var layer = group.getLayer();
anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: "#666",
fill: "#ddd",
strokeWidth: 2,
radius: 8,
name: name,
draggable: true
});
anchor.on("dragmove", function() {
update(group, this);
layer.draw();
});
anchor.on("mousedown touchstart", function() {
group.setDraggable(false);
this.moveToTop();
});
anchor.on("dragend", function() {
group.setDraggable(true);
layer.draw();
});
// add hover styling
anchor.on("mouseover", function() {
var layer = this.getLayer();
document.body.style.cursor = "pointer";
this.setStrokeWidth(4);
layer.draw();
});
anchor.on("mouseout", function() {
var layer = this.getLayer();
document.body.style.cursor = "default";
this.setStrokeWidth(2);
layer.draw();
});
group.add(anchor);
}
I would like to turn them into arrows, or something similar to show users that infact the image is resizeable. Does anyone have a method of doing this or a tutorial that may show me how to either draw arrows or replace the anchors with an image?
Thanks for any help...
I think you'll need to change the new Kinetic.Cirle and it's properties to something like this:
var anchor = new Kinetic.RegularPolygon({
x: x,
y: y,
sides: 3,
rotation: -190,
radius: 8,
stroke: "black",
strokeWidth: 2,
name: name,
draggable: true
});
Although this is only the beginning, due to having a different rotation on each anchor, you will also have to add more variables to the group of anchors as to have each triangle face the correct direction.
I have only briefly tested this, but I hope it helps as a starting point.
Remember to double check the Docs.
EDIT: Also see here.