Get screen coordinates of an Kinetic image in Javascript? - javascript

I'm writing an app and using Kinetic JS library to load image from client's computer. The image is contained in a photo object and is initialised as below
var photoObj = new Image();
photoObj.onload = function() {
var photoImage = new Kinetic.Image({
x: 0,
y: 0,
image: photoObj,
width: photoObj.width,
height: photoObj.height,
name: "photo",
id: "photo"
});
photoGroup.removeChildren();
photoGroup.add(photoImage);
The object is included in a Kinetic Group object, named photoGroup, which is created earlier.
photoGroup = new Kinetic.Group({
x : 0,
y : 0,
draggable : true,
id : "photoGroup"
});
photoLayer = new Kinetic.Layer({
drawBorder: true
});
photoLayer.add(photoGroup);
stage = new Kinetic.Stage({
container : "kinetic-kard-preview",
width : 320,
height : 480
});
stage.add(photoLayer);
stage.draw();
The group object is included in a Kinetic Layer object, named photoLayer, which is included in a Kinetic Stage object, named stage.
Anyway, my question is, now I would like to get the screen coordinates of the image. I successfully got the coordinates of the stage by
var containerOffset=$("#kinetic-kard-preview").offset();
var offsetX=containerOffset.left;
var offsetY=containerOffset.top;
console.log(offsetX);//455.859375
console.log(offsetY);//218
but I can't seem to do the same thing for the image. When I viewed source code of the web page, I've noticed that the generated html code of the canvas that contains the image is something like this.
<canvas width="320" height="480" style="padding: 0px; margin: 0px; border: 0px; background-color: transparent; width: 320px; height: 480px; position: absolute; background-position: initial initial; background-repeat: initial initial;"></canvas>
How can I get the screen coordinates of the image after it's loaded from client's pc to the canvas? Please help!!! Thank you.
thanks for your answer but it can't seem to get what I need. I just added the link of the screenshot here:
As you can see in the picture, the coordinates of the image are not the same as the coordinates of the stage that contains it. I wrote a mousemove event and calculated
- the distance between the x screen coordinate of the stage (ie. offsetX) and x screen coordinate of the image.
- the distance between the y screen coordinate of the stage (ie. offsetY) and y screen coordinate of the image.
They all have the same number....24 (as shown on the screen). By using the mousemove event, I manually got the values of offsetX, offsetY and the x and y screen coordinates of the image are 455, 218, 479 and 242.
However, my problem is how to get those numbers automatically, especially the screen coordinates of the image, after the image is loaded from my pc. I printed the values of gPos to console.log and got (0,0) values. So when I added offsetX and offsetY to these values, they are the same (455,218), not (479,242). Please let me know what should I do now? Thanks again for your help.

Your Kinetic.Image is at position [0,0] in a draggable Kinetic.Group.
So you can get your image position by adding:
The #kinetic-kard-preview offsets
Plus the current group position: group.position()
[ Added code for KineticJS version 4.5.1 ]
Here is example code for v4.5.1 and a Demo: http://jsfiddle.net/m1erickson/NXH9X/
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype</title>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.5.1.min.js"></script>
<style>
body{padding:20px; background:ivory;}
#kinetic-kard-preview{
margin-top: 10px;
width:350px;
height:350px;
border:1px solid gray;
}
#target{position:absolute;top:200px;left:150px;opacity:0.50;}
</style>
<script>
$(function(){
//
$pos=$("#results");
//
var containerOffset=$("#kinetic-kard-preview").offset();
var offsetX=containerOffset.left;
var offsetY=containerOffset.top;
//
var stage = new Kinetic.Stage({
container: 'kinetic-kard-preview',
width: 350,
height: 350
});
//
photoLayer = new Kinetic.Layer({
drawBorder: true
});
stage.add(photoLayer);
//
photoGroup = new Kinetic.Group({
x : 0,
y : 0,
draggable : true,
id : "photoGroup"
});
photoGroup.on("dragmove",function(){
var gPos=photoGroup.getPosition();
var x=parseInt(offsetX+gPos.x);
var y=parseInt(offsetY+gPos.y);
$pos.text("photoImage: screenX="+x+", screenY="+y);
});
photoLayer.add(photoGroup);
//
var photoObj = new Image();
photoObj.onload = function() {
var photoImage = new Kinetic.Image({
x: 0,
y: 0,
image: photoObj,
width: photoObj.width,
height: photoObj.height,
name: "photo",
id: "photo"
});
photoGroup.add(photoImage);
photoLayer.draw();
}
photoObj.src="https://dl.dropboxusercontent.com/u/139992952/multple/norwayFlag.jpg";
}); // end $(function(){});
</script>
</head>
<body>
<h4>Semi-transparent flag is at screen position 150,200</h4>
<p id=results>Drag the opaque flag and see screen position</p>
<img id=target src='https://dl.dropboxusercontent.com/u/139992952/multple/norwayFlag.jpg'>
<div id="kinetic-kard-preview"></div>
</body>
</html>

Related

How do I restrict an image to another image in fabricjs?

I have a frame image with a transparent center and a button that draws a local image to behind that frame (so it looks framed). How can I confine the uploaded image to the frame? I've tried using a frame image without a transparent surrounding area however I'd eventually like to use backgrounds.
I'm working with different shaped frames that are different sizes. How might I accomplish this? Any help would be greatly appreciated. I've been tinkering with this for days. I'll attach my code and an image to help better show the problem. Thanks in advance!
var canvas = new fabric.Canvas('c');
document.getElementById('file').addEventListener("change", function(e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function(f) {
var data = f.target.result;
fabric.Image.fromURL(data, function(img) {
var oImg = img.set({
scaleX: 0.8,
scaleY: 0.8,
left: 430,
top: 65
})
canvas.add(oImg);
canvas.setActiveObject(oImg);
var image = canvas.getActiveObject();
image.moveTo(-1);
canvas.discardActiveObject();
canvas.renderAll();
});
};
reader.readAsDataURL(file);
});
fabric.Image.fromURL('https://i.imgur.com/OeXL2CJ.png', function(img) {
var oImg = img.set({
scaleX: 0.5,
scaleY: 0.5,
selectable: false
})
canvas.add(oImg).renderAll();
});
canvas {
border: 1px solid #808080;
border-radius: 1px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.min.js"></script>
<input type="file" id="file"><br />
<canvas id="c" width="637" height="412"></canvas>
Here you go mate. The quintessence of the answer is the same:
var pug = new fabric.Image(pugImg, {
width: 500,
height: 300,
cropX: 0,
cropY: 0,
left: 10,
top: 10
});
But maybe it is more comprehensive with a fiddle. And it is a lovely Pug ;-)
http://jsfiddle.net/sharksinn/5vyevd5y/
And please flag as the correct answer if this fixes you problem :-)
If I understand you right, you would like to show your second image only in the middle area of your frame image. You should be able to do this with the new Fabrc 2 beta. Fabric 2 introduces some new parameters for image onbects: scaleX, scaleY, width, height, cropX, cropY...
Setting the object origin of both images to center/middle and adjusting the width/height and cropX/cropY of your second image should do the trick.
Here is the documentation for the new features:
http://fabricjs.com/v2-breaking-changes#image

Adding canvas inside another canvas: obj.setCoords is not a function( fabric js)

Started using fabric.js and trying to add a canvas inside another canvas, so that the top canvas stays constant and I'll add objects to inner canvas.
Here is the snippet of adding a canvas to another canvas.
canvas = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
canvas.add(innerCanvas);
and my html looks like this
<canvas id="artcanvas" width="500" height="500"></canvas>
<canvas id="innerCanvas" width="200" height="200" ></canvas>
Once adding these successfully, what I am going to do is , add the coordinates to the inner canvas, so that it looks like one on another to the end user.
However, ran into the below error for the tried code
Uncaught TypeError: obj.setCoords is not a function
at klass._onObjectAdded (fabric.js:6894)
at klass.add (fabric.js:231)
at main.js:60
at fabric.js:19435
at HTMLImageElement.fabric.util.loadImage.img.onload (fabric.js:754)
_onObjectAdded # fabric.js:6894
add # fabric.js:231
(anonymous) # main.js:60
(anonymous) # fabric.js:19435
fabric.util.loadImage.img.onload # fabric.js:754
Looking at the error message, just went to the line of error and here is what I found in chrome console
Can someone point the mistake in my codes ?
After going through no.of discussions and internet solutions, for time being I am using Fabric Rectangle as a clipper and setting it's boundaries so user can be able to drop/play with in that particular clipper.
Dotted red(image below) is my clipper and now I can bound the dropping and below is the code to add an image with a clipper.
function addImageToCanvas(imgSrc) {
fabric.Object.prototype.transparentCorners = false;
fabric.Image.fromURL(imgSrc, function(myImg) {
var img1 = myImg.set({
left: 20,
top: 20,
width: 460,
height: 460
});
img1.selectable = false;
canvas.add(img1);
var clipRectangle = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 150,
top: 150,
width: 200,
height: 200,
fill: 'transparent',
/* use transparent for no fill */
strokeDashArray: [10, 10],
stroke: 'red',
selectable: false
});
clipRectangle.set({
clipFor: 'layer'
});
canvas.add(clipRectangle);
});
}
Now while appending any image/layer to the canvas, I bind that image/layer/text to the clipper I created.
function addLayerToCanvas(laImg) {
var height = $(laImg).height();
var width = $(laImg).width();
var clickedImage = new Image();
clickedImage.onload = function(img) {
var pug = new fabric.Image(clickedImage, {
width: width,
height: height,
left: 150,
top: 150,
clipName: 'layer',
clipTo: function(ctx) {
return _.bind(clipByName, pug)(ctx)
}
});
canvas.add(pug);
};
clickedImage.src = $(laImg).attr("src");
}
And the looks like, after restriction of bounds,
Here is the fiddle I have created with some static image url.
https://jsfiddle.net/sureshatta/yxuoav39/
So I am staying with this solution for now and I really feel like this is hacky and dirty. Looking for some other clean solutions.
As far as I know you can't add a canvas to another canvas - you're getting that error as it tries to call setCoords() on the object you've added, but in this case it's another canvas and fabric.Canvas doesn't contain that method (see docs). I think a better approach would be to have two canvases and position them relatively using CSS - see this simple fiddle
HTML
<div class="parent">
<div class="artcanvas">
<canvas id="artcanvas" width="500" height="500"></canvas>
</div>
<div class="innerCanvas">
<canvas id="innerCanvas" width="200" height="200" ></canvas>
</div>
</div>
CSS
.parent {
position: relative;
background: black;
}
.artcanvas {
position: absolute;
left: 0;
top: 0;
}
.innerCanvas {
position: absolute;
left: 150px;
top: 150px;
}
JS
$(document).ready(function() {
canvas = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
var rect = new fabric.Rect({
fill: 'grey',
width: 500,
height: 500
});
canvas.add(rect);
var rect2 = new fabric.Rect({
fill: 'green',
width: 200,
height: 200
});
innerCanvas.add(rect2);
})
To handle the object serialization, you can do something like this:
var innerObjs = innerCanvas.toObject();
console.dir(innerObjs);
var outerObjs = canvas.toObject();
innerObjs.objects.forEach(function (obj) {
obj.left += leftOffset; // offset of inner canvas
obj.top += topOffset;
outerObjs.objects.push(obj);
});
var json = JSON.stringify(outerObjs);
This will then give you the JSON for all objects on both canvases
I have no understanding why you want to do this thing, but to put a canvas inside another canvas, you have one simple way:
canvas = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
imageContainer = new fabric.Image(innerCanvas.lowerCanvasEl);
canvas.add(imageContainer);
Then depending what you want to do, you may need additional tweaks, but this should work out of the box.
Don't create a canvas
Most objects in fabric (from my limited experience) are at some point converted to a canvas. Creating an additional fabric canvas to manage a group of objects is kind of pointless as you are just adding overhead and mimicking fabrics built in groups object.
Fabric objects are basically DOM canvases wrappers.
The following example shows how fabric uses a canvas to store the content of a group. The demo creates a group and adds it to the fabric canvas, then gets the groups canvas and adds it to the DOM. Clicking on the group's canvas will add a circle. Note how it grows to accommodate the new circles.
const radius = 50;
const fill = "#0F0";
var pos = 60;
const canvas = new fabric.Canvas('fCanvas');
// create a fabric group and add two circles
const group = new fabric.Group([
new fabric.Circle({radius, top : 5, fill, left : 20 }),
new fabric.Circle({radius, top : 5, fill, left : 120 })
], { left: 0, top: 0 });
// add group to the fabric canvas;
canvas.add(group);
// get the groups canvas and add it to the DOM
document.body.appendChild(group._cacheContext.canvas);
// add event listener to add circles to the group
group._cacheContext.canvas.addEventListener("click",(e)=>{
group.addWithUpdate(
new fabric.Circle({radius, top : pos, fill : "blue", left : 60 })
);
canvas.renderAll();
pos += 60;
});
canvas {
border : 2px solid black;
}
div {
margin : 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.13/fabric.min.js"></script>
<div>Fabric's canvas "canvas = new fabric.Canvas('fCanvas');"</div>
<canvas id="fCanvas" width="256" height="140"></canvas>
<div>Fabric group canvas below. Click on it to add a circle.</div>
Use a group rather than a new instance of a fabric canvas.
As you can see a canvas is generated for you. Adding another fabric canvas (Note that a fabric canvas is not the same as a DOM canvas) will only add more work for fabric to do, which already has a lot of work to do.
You are best of to use a group and have that hold the content of the other fabric object you wish to shadow. That would also contain its content in a group.
Just an image
And just a side not, a DOM canvas is an image and can be used by fabric just as any other image. It is sometimes better to do the rendering directly to the canvas rather than via fabric so you can avoid rendering overheads that fabric needs to operate.
To add a DOM canvas to fabric just add it as an image. The border and text are not fabric object, and apart from the code to render them take up no memory, and no additional CPU overhead that would be incurred if you used a fabric canvas and objects.
const canvas = new fabric.Canvas('fCanvas');
// create a standard DOM canvas
const myImage = document.createElement("canvas");
// size it add borders and text
myImage.width = myImage.height = 256;
const ctx = myImage.getContext("2d");
ctx.fillRect(0,0,256,256);
ctx.fillStyle = "white";
ctx.fillRect(4,4,248,248);
ctx.fillStyle = "black";
ctx.font = "32px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("The DOM canvas!",128,128);
// use the canvas to create a fabric image and add it to fabrics canvas.
canvas.add( new fabric.Image(myImage, {
left: (400 - 256) / 2,
top: (400 - 256) / 2,
}));
canvas {
border : 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.13/fabric.min.js"></script>
<canvas id="fCanvas" width="400" height="400"></canvas>
innerCanvas.setCoords(); ìs a function, but you need it only after you set the coordinates. Or more precise, set these four elements:
innerCanvas.scaleX = 1;
innerCanvas.scaleY = 1;
innerCanvas.left = 150;
innerCanvas.top = 150;
innerCanvas.setCoords();
canvas.renderAll();

How to get Co-ordinates of image element in canvas

I am trying to drag drop image in canvas and i am getting perfect co-ordinates of that image in canvas.But whenever i move that same image in the canvas i am not getting the new co-ordinates of that image. I want help to determine the new co-ordinates of that image when i move it.
this is my code:
// get the offset position of the kinetic container
var $stageContainer=$("#container");
var stageOffset=$stageContainer.offset();
var offsetX=stageOffset.left;
var offsetY=stageOffset.top;
// create the Kinetic.Stage and layer
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var layer = new Kinetic.Layer();
stage.add(layer);
// start loading the image used in the draggable toolbar element
// this image will be used in a new Kinetic.Image
var image1=new Image();
image1.onload=function(){
$house.show();
}
image1.src="https://dl.dropboxusercontent.com/u/139992952/multple/4top.png";
// make the toolbar image draggable
$house.draggable({
helper:'clone',
});
// set the data payload
$house.data("url","house.png"); // key-value pair
$house.data("width","32"); // key-value pair
$house.data("height","33"); // key-value pair
$house.data("image",image1); // key-value pair
// make the Kinetic Container a dropzone
$stageContainer.droppable({
drop:dragDrop,
});
// handle a drop into the Kinetic container
function dragDrop(e,ui){
// get the drop point
var x=parseInt(ui.offset.left-offsetX);
var y=parseInt(ui.offset.top-offsetY);
// get the drop payload (here the payload is the image)
var element=ui.draggable;
var data=element.data("url");
var theImage=element.data("image");
// create a new Kinetic.Image at the drop point
// be sure to adjust for any border width (here border==1)
var image = new Kinetic.Image({
name:data,
x:x,
y:y,
image:theImage,
draggable: true
});
layer.add(image);
layer.draw();
}
body{padding:20px;}
#container{
border:solid 1px #ccc;
margin-top: 10px;
width:350px;
height:350px;
}
#toolbar{
width:350px;
height:35px;
border:solid 1px blue;
}
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.7.2.min.js"></script>
<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.min.js"></script>
<h4>Drag from toolbar onto canvas. Then drag around canvas.</h4>
<div id="toolbar">
<img id="house" width=32 height=32 src="https://dl.dropboxusercontent.com/u/139992952/multple/4top.png"><br>
</div>
<div id="container"></div>
Each KineticJS object, like the image object, has x,y properties that hold its current location.
In recent versions of KineticJS, you can fetch an object's x,y using:
var x=myImageObject.x();
var y=myImageObject.y();
Or you can fetch an object containing the current x,y like this:
// myXY has x,y properties: myXY.x and myXY.y
var myXY=myImageObject.position();

Distorting a shape in Canvas by Clicking and Dragging a particular border point

I'm creating a floor-plan application using HTML5 and kinetic js.Currently I'm able to create a rectangular container where you can drag and drop objects inside it. You can see it in action in this link http://jsfiddle.net/wQ8YA/29/
The js code to create it is..
//create a group
var group = new Kinetic.Group({
draggable: true //make group draggable
});
var rec = new Kinetic.Rect({
x: 10,
y: 330,
width: 600,
height: 600
});
group.add(rec);
Currently the container looks like this
But i want to make it like this..
In the second image the user can distort the container by clicking on any point on the container border and dragging it.I tried doing it on my own but don't know where to start,what this concept is called and how to achieve it.Please Help..
Basics of how it's done:
Your bottom image appears to be a set of 4 cubic Bezier curves forming a rectangle.
The squares on the corners of the rectangle set the starting and ending points of the curve.
The circles set the 2 control points of the curve.
If the control points are positioned on the imaginary line between the starting and ending points then the curve becomes a line segment connecting the starting and ending points.
If you add code to allow the user to drag the starting, ending and control points, you will have the functionality in your bottom image.
You can use Kinetic.Rect, Kinetic.Circle and Kinetic.Line to allow the user to drag the control points.
Since KineticJS does not have a Cubic Bezier Curve shape, you will have to use the Kinetic.Shape to custom draw the 4 curves based on the positions of the control points.
Here's a simple example with 1 anchor that drag-alters the top-right corner:
http://jsfiddle.net/m1erickson/t2mhpmrs/
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype</title>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.1.min.js"></script>
<style>
body{padding:20px;}
#container{
border:solid 1px #ccc;
margin-top: 10px;
width:350px;
height:350px;
}
</style>
<script>
$(function(){
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var layer = new Kinetic.Layer();
stage.add(layer);
var g=new Kinetic.Group({
x:100,y:100,
});
layer.add(g);
var room=new Kinetic.Shape({
x:0,
y:0,
width:100,
height:100,
stroke:"blue",
fill: 'red',
drawFunc: function(context) {
var x=this.x();
var y=this.y();
var w=this.width();
var h=this.height();
var right=anchorTR.x();
var top=anchorTR.y();
context.beginPath();
context.moveTo(x,y);
// top
context.bezierCurveTo(x+w/3,y,x+w*2/3,y,right,top);
// right
context.bezierCurveTo(x+w,y+h/3, x+w,y+h*2/3, x+w,y+h);
// bottom
context.bezierCurveTo(x+w*2/3,y+h, x+w/3,y+h, x,y+h);
// left
context.bezierCurveTo(x,y+h*2/3, x,y+h/3, x,y);
context.fillStrokeShape(this);
}
});
g.add(room);
var anchorTR = new Kinetic.Circle({
x:100,
y:0,
radius:8,
fill:"green",
stroke: 'black',
strokeWidth: 1,
draggable: true
});
g.add(anchorTR);
layer.draw();
}); // end $(function(){});
</script>
</head>
<body>
<h4>Drag green circle to change red rect</h4>
<div id="container"></div>
</body>
</html>

Allowing the drag after dropping an image onto canvas

$(function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// get the offset position of the container
var $canvas = $("#canvas");
var Offset = $canvas.offset();
var offsetX = Offset.left;
var offsetY = Offset.top;
// select all .tool's
var $tools = $(".tool");
// make all .tool's draggable
$tools.draggable({
helper: 'clone',
revert: 'invalid'
});
// assign each .tool its index in $tools
$tools.each(function (index, element) {
$(this).data("toolsIndex", index);
});
// make the canvas a dropzone
$canvas.droppable({
drop: dragDrop,
});
// handle a drop into the canvas
function dragDrop(e, ui) {
// get the drop point (be sure to adjust for border)
var x = parseInt(ui.offset.left - offsetX);
var y = parseInt(ui.offset.top - offsetY);
// get the drop payload (here the payload is the $tools index)
var theIndex = ui.draggable.data("toolsIndex");
// drawImage at the drop point using the dropped image
ctx.drawImage($tools[theIndex], x, y, 32, 32);
}
});
I tried many things but I failed. This code allows me to drag and drop multiple images onto a canvas element. What I need to do is to add the possibility of dragging the image again after it's been dropped. I know that the canvas has to be redrawn each time, but I didn't know how.
Can anyone fix this for me?
Since you commented that you're open to canvas libraries, here's an example that lets you:
drag an img element from a toolbar-div using jqueryUI.
drop the img on the canvas and create a KineticJS.Image object that you can drag around the canvas.
A Demo: http://jsfiddle.net/m1erickson/gkefk/
Results: An img dragged 3X from blue toolbar, dropped on gray canvas, and then dragged on the canvas.
Here's a commented code example:
$(function() {
// get a reference to the house icon in the toolbar
// hide the icon until its image has loaded
var $house = $("#house");
$house.hide();
// get the offset position of the kinetic container
var $stageContainer = $("#container");
var stageOffset = $stageContainer.offset();
var offsetX = stageOffset.left;
var offsetY = stageOffset.top;
// create the Kinetic.Stage and layer
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var layer = new Kinetic.Layer();
stage.add(layer);
// start loading the image used in the draggable toolbar element
// this image will be used in a new Kinetic.Image
var image1 = new Image();
image1.onload = function() {
$house.show();
}
image1.src = "https://i.stack.imgur.com/GeibZ.png";
// make the toolbar image draggable
$house.draggable({
helper: 'clone',
});
// set the data payload
$house.data("url", "house.png"); // key-value pair
$house.data("width", "32"); // key-value pair
$house.data("height", "33"); // key-value pair
$house.data("image", image1); // key-value pair
// make the Kinetic Container a dropzone
$stageContainer.droppable({
drop: dragDrop,
});
// handle a drop into the Kinetic container
function dragDrop(e, ui) {
// get the drop point
var x = parseInt(ui.offset.left - offsetX);
var y = parseInt(ui.offset.top - offsetY);
// get the drop payload (here the payload is the image)
var element = ui.draggable;
var data = element.data("url");
var theImage = element.data("image");
// create a new Kinetic.Image at the drop point
// be sure to adjust for any border width (here border==1)
var image = new Kinetic.Image({
name: data,
x: x,
y: y,
image: theImage,
draggable: true
});
layer.add(image);
layer.draw();
}
}); // end $(function(){});
body {
padding: 20px;
}
#container {
border: solid 1px #ccc;
margin-top: 10px;
width: 350px;
height: 350px;
}
#toolbar {
width: 350px;
height: 35px;
border: solid 1px blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/kineticjs/4.7.2/kinetic.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype</title>
<script
src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
crossorigin="anonymous"></script>
</head>
<body>
<div id="toolbar">
<img id="house" width=32 height=32 src="https://i.stack.imgur.com/GeibZ.png"><br>
</div>
<div id="container"></div>
</body>
</html>
What you want is certainly not easy. Now you just drop the image and draw it in the mouse position. To do what you want, you'll need:
Keep track of the images added, their positions, their sizes, and their z-index. The best way to do this is using a stack structure, an array of objects with this properties: url x, y, width, height. The z-index can be the index of the array.
Once you start a drag operation on the canvas, you need to get the point you're dragging, and find the image with the highest z-index that contains that point (basically, implement hit-testing).
To move it, then you have to remove it from the canvas, which implies redrawing the entire canvas with all the images except the one you're dragging. For this you can use the stack previously defined, and draw the images in order.
Finally, you need to draw your image again once you drop it, take it from its position in the array and append it at the end.
This is not an easy task. I suggest you to use some library for it. I cannot recommend you one, because I have little to no experience with canvas.

Categories