I'm working on a simple diagram editor using KineticJS. I would like to use two separate canvases for the palette area (that contains a number of Kinetic.Groups that represent the different nodes of the network I might create), and the diagramming area where I can add nodes from the palette via drag and drop, and then add connections between the various nodes as specific anchor points. I'm having trouble figuring out the drag and drop process from the palette canvas of (stationary) Kinetic.Groups over to the other canvas containing diagramming area. I'm guessing that I need to fire off of the dragstart event for the palette objects (although I don't want these themselves to be draggable), and then do something like create an opague copy of the palette object that can be dragged around, and finally dropped into the diagramming area (with full opacity).
Can groups be dragged outside of a the staging canvases boundaries? Maybe I need to generate an image when I start to drag from the palette, drag that image over, and then create another group when dropping into the diagramming area.
Does any one know of any examples that might point me in the right direction, or who can offer some insight (even code) into the required process. I've searched the KineticJS examples but can't quite find enough to get me going.
Here’s one way to drag nodes from a source palette into a destination group:
A Fiddle: http://jsfiddle.net/m1erickson/xtVyL/
Network nodes are represented by small icons (which are really small kinetic image objects).
The user can drag any icon from the source palette to any destination group.
The groups are just defined areas on the canvas, but could be Kinetc.Groups for more flexibility.
During the dragend event, a new duplicate copy of the dragged icon is created in the destination group.
After the dragend event is complete, the original palette icon is automatically moved from
The newly created duplicate icon can be dragged around the destination group (but not outside that group).
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/xtVyL/
<!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.5.min.js"></script>
<style>
#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);
// image loader
var imageURLs=[];
var imagesOK=0;
var imgs=[];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/tempPC.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/tempServer.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/tempRouter.png");
loadAllImages();
function loadAllImages(callback){
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK==imageURLs.length ) {
start();
}
};
img.src = imageURLs[i];
}
}
// top icon positions
var nextIconX=20;
var nextIconY=20;
// define groups
var groups=[];
groups.push({x:0,y:100,w:175,h:250,fill:"skyblue"});
groups.push({x:175,y:100,w:175,h:250,fill:"cornsilk"});
// add boundary info to each group
// draw colored rect to show group area
for(var i=0;i<groups.length;i++){
var g=groups[i];
g.left=g.x;
g.right=g.x+g.w;
g.top=g.y;
g.bottom=g.y+g.h;
var rect=new Kinetic.Rect({
x:g.x,
y:g.y,
width:g.w,
height:g.h,
fill:g.fill,
stroke:"gray"
});
layer.add(rect);
}
// hittest for each group
function groupHit(x,y){
for(var i=0;i<groups.length;i++){
var g=groups[i];
if(x>g.left && x<g.right && y>g.top && y<g.bottom){return(i);}
}
return(-1);
}
function start(){
makePaletteIcon(imgs[0]);
makePaletteIcon(imgs[1]);
makePaletteIcon(imgs[2]);
layer.draw();
}
function makePaletteIcon(img){
// make an icon that stays in the pallette tray
var fixedIcon=newImage(nextIconX,nextIconY,img,false);
layer.add(fixedIcon);
// make an icon that is dragged from the tray to a group
var dragIcon=makeDraggableIcon(nextIconX,nextIconY,img);
layer.add(dragIcon);
// calc the next icon position
nextIconX+=(img.width+20);
}
function makeDraggableIcon(x,y,img){
var i=newImage(x,y,img,true);
//
i.trayX=x;
i.trayY=y;
//
i.setOpacity(0.50);
i.on("dragend",function(){
var x=this.getX();
var y=this.getY();
// if this pallette icon was not dropped in a group
// put the icon back in the tray and return
var hit=groupHit(x,y);
if(hit==-1){
this.setPosition(this.trayX,this.trayY);
return;
}
// add a copy of this icon to the drop group
var component=newImage(x,y,this.getImage(),true);
// set drag limits
var group=groups[hit];
component.maxDragLeft=group.left;
component.maxDragRight=group.right;
component.maxDragTop=group.top;
component.maxDragBottom=group.bottom;
// limit component dragging to inside the assigned group
component.setDragBoundFunc(function(pos) {
var xx=pos.x;
var yy=pos.y;
var w=this.getWidth();
var h=this.getHeight();
if(pos.x<this.maxDragLeft){xx=this.maxDragLeft;}
if(pos.x+w>this.maxDragRight){xx=this.maxDragRight-w;}
if(pos.y<this.maxDragTop){yy=this.maxDragTop;}
if(pos.y+h>this.maxDragBottom){yy=this.maxDragBottom-h;}
return{ x:xx, y:yy };
});
layer.add(component);
// move the dragIcon back into the pallette tray
this.setPosition(this.trayX,this.trayY);
layer.draw();
});
return(i);
}
// make a new Kinetic.Image
function newImage(x,y,img,isDraggable){
var i=new Kinetic.Image({
image:img,
x: x,
y: y,
width: img.width,
height: img.height,
draggable:isDraggable
});
return(i);
}
}); // end $(function(){});
</script>
</head>
<body>
<p>Drag any icon from top into blue or yellow group</p>
<div id="container"></div>
</body>
</html>
Related
I need to build a drawing board with erase function using PaperJS. The drawing board need to have a background image and people can draw on the canvas or erase existing path.
//erase function
function onMouseDown(event) {
path = new paper.Path();
path.strokeColor = 'red';
path.strokeWidth = 10;
path.strokeCap = 'round';
path.blendMode = 'destination-out';
}
function onMouseDrag(event) {
path.add(event.point);
}
//setup background image
var bgimage = "./cat.jpg";
var raster = new paper.Raster({
source: bgimage,
position: paper.view.center
});
The problem of using this method is that the background image will also be erased.
Erase Result: http://i.stack.imgur.com/0D4TU.png
JSFiddle: https://jsfiddle.net/9kaffsg4/2/
Is there any way to erase the existing path, without erase the background image?
If your background image does not change: you can put it in an <img> bellow your <canvas>?
If you want to be able to edit your image: you can redraw the image, then your drawing (treated separately), every frame.
I have solved the problem by using two canvas (one is for basic drawing function, the other is the background canvas). And then overlap the two canvas.
I expanded on your initial code example so that I could demonstrate using layers as a solution. Basically, three layers are created. They are created in the order you see them in (1:background, 2:foreground, 3:clearBtn). The layers created earlier are visualy "underneath" the layers created later.
In this way, you can keep your static raster graphic on the bottom-most layer (background), create your "eraser" paths on the middle layer (foreground), and you can remove/clear these "eraser" paths by clicking on the "clear" button. The clear button is on the top-most layer so that it doesn't become obscured by the "eraser paths". I just added the clear button to emphasize the added organizational control that layers provide.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.9.12/paper.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<style type="text/css">
canvas {
border: 1px solid #000;
}
</style>
<script type="text/paperscript" canvas="myCanvas">
var layers = {
"background": new Layer(),
"foreground": new Layer(),
"clearBtn": new Layer()
};
var paths = [];
//erase function
function onMouseDown(event) {
layers.foreground.activate();
path = new paper.Path();
path.strokeColor = 'red';
path.strokeWidth = 10;
path.strokeCap = 'round';
path.blendMode = 'destination-out';
paths.push(path);
}
function onMouseDrag(event) {
layers.foreground.activate();
path.add(event.point);
}
//setup background image
layers.background.activate();
var bgimage = "http://placehold.it/300x150";
var raster = new paper.Raster({
source: bgimage,
position: paper.view.center
});
// Build a "clear button"
// Clicking this will remove all children
// from the foreground layer
layers.clearBtn.activate();
var clearBtn = new Path.Rectangle({
point: new Point(250, 2),
size: new Size(50, 20),
style: {
fillColor: 'white',
strokeColor: 'black'
}
});
var txt = new PointText({
point: clearBtn.position + new Point(0,4),
content: 'Clear',
justification: 'center'
});
var clearBtnGrp = new Group({
children: [clearBtn, txt]
});
clearBtnGrp.on('mousedown', function () {
for ( var i = 0; i < paths.length; i++ ) {
paths[i].remove();
}
});
</script>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>
Edit I updated this to cache paths in an array because the previous example did not always clear all layer children.
I have three questions. The first question is the most important but I appreciate to get answers to the others.
First question, see this jsfiddle:
https://jsfiddle.net/owLdgrdq/10
//copia nariz
$(document).on('click', '#add-nariz', function(){
var a = $('#add-nariz');
var src = a.attr('src');
var elem = $('<img class="objetos" src="' + src + '" width="30px" height="30px" style="positon: relative;" />');
$('#fotoAlterada').append(elem);
elem.draggable();
});
//copia bolinha azul
$(document).on('click', '#add-bb', function(){
var a = $('#add-bb');
var src = a.find('img:first').attr('src');
var elem = $('<img class="objetos" src="' + src + '" width="30px" height="30px" style="positon: relative;" />');
$('#fotoAlterada').append(elem);
elem.draggable();
});
$(document).on('click', '#salvaImagem', function(){
var m = $('#foto');
var totX = parseInt(m.css('width'));
var totY = parseInt(m.css('height'));
var c = document.getElementById('myCanvas');
c.width = totX;
c.height = totY;
var ctx = c.getContext('2d');
alert(totX + '\n' + totY);
var base = document.getElementById('foto');
ctx.drawImage(base,0,0,totX,totY,0,0,totX,totY);
var posicoes = [];
$(".objetos").each(function(){
var img = $(this);
x = parseInt(img.css("left"))+totX;
y = parseInt(img.css("top"))+totY;
altura = parseInt(img.css("width"));
largura = parseInt(img.css("height"));
posicoes.push([
x,
y,
largura,
altura
]);
});
alert( JSON.stringify(posicoes));
var j;
var numAderecos = posicoes.length;
for(j = 0; j < numAderecos; j++){
ctx.drawImage(base,posicoes[j][0],posicoes[j][1],posicoes[j][2],posicoes[j][3]);
}
});
I have some icons (Adicionar isto:) what I want add to a photo (Editar isto:). When the user clicks on the icons, a copy is made beside the photo. More click make more copies. These copies are draggable. The user choose the position of the objects and click in a button to save ("Salvar Mudanças"). This button makes a copy of the original photo with the new objects on it (inside a canvas).
At final of the JS code I put little photos (same as original) as little objects added to the photo in the canvas, but just because I don't know how to copy each object added. How can I do that?
Second question, when click on the button, the positions are not the same in the canvas, they are pull to down a little (and left too). Why is this happening if I put padding and margin 0px in CSS code?
Third question, the variable 'base' I used $('#foto') to get by id, but I can't use it at canvas arguments. When I use document.getElementById('foto') I can. What is the difference among them?
----editing ----
My aim with this is to make a mobile application, through cordova/phonegap. The user must be able to get a picture from the device or a selfie with the cam (it's already ok). When the picture/photo is gotten, some new objects like a crown's nose, colorful hat and other silly things appear on the window (it's ok, just change style="display: none;" to be displayed), when the user clicks on these things they make a copy at the bottom of the photo and these copies must be draggable (it's nice too). Then, with jQuery-UI I'm getting the position of all dragged objects into the photo, the user clicks on a button "Salvar Mudanças" ("save changes"), and this action copy the photo plus objects to a canvas area (this is the way I got to make the app). After, the user clicks on another button to share the modified image (in canvas) through whatsapp, facebook, email and any other way which the device could do.
Here is an alternate way of appending icons onto an image using just a canvas
Carve out a toolbar area at the top of an html5 canvas and fill it with your desired icons.
Put the destination image below the toolbar.
Listen for mouse events.
Allow the user to icons from the toolbar onto the image below.
When the user "drops" the dragging icon by releasing the mouse, create a duplicate of the dragged icon at the dropped position.
Here is annotated code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
var isDown=false;
var startX,startY;
ctx.lineWidth=0.50;
ctx.strokeStyle='lightgray';
ctx.fillStyle='white';
// vars for icons, etc
var tbar={width:cw,height:35}
var tw=30;
var th=30;
var dragging=null;
var dropped=[];
var icons=[
{name:'Flower',x:tw*0,y:2,url:'https://dl.dropboxusercontent.com/u/139992952/multple/flower.png'},
{name:'Star',x:tw*1,y:2,url:'https://dl.dropboxusercontent.com/u/139992952/multple/star.png'},
{name:'Plane',x:tw*2,y:2,url:'https://dl.dropboxusercontent.com/u/139992952/multple/plane2.png'},
{name:'Mario',x:tw*3,y:2,url:'https://dl.dropboxusercontent.com/u/139992952/multple/marioStanding.png'},
];
var thumbs=[];
var mainImg=new Image();
mainImg.crossOrigin='anonymous';
mainImg.onload=start;
mainImg.src='https://dl.dropboxusercontent.com/u/139992952/multple/husky.jpg';
var imgCount=icons.length+1;
for(var i=0;i<icons.length;i++){
var icon=icons[i];
icon.index=i;
icon.img=new Image();
icon.img.crossOrigin='anonymous';
icon.img.onload=start;
icon.img.src=icon.url;
}
// start is called when each image is fully loaded
function start(){
// wait for all images to load
if(--imgCount>0){return;}
// create
for(var i=0;i<icons.length;i++){
var icon=icons[i];
thumbs.push(thumb(icon.img,tw,th));
}
// draw the toolbar & image
draw();
// listen for mouse events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUpOut(e);});
$("#canvas").mouseout(function(e){handleMouseUpOut(e);});
}
// create thumbnails for each image (to be used as icons in the toolbar)
function thumb(img,w,h){
var iw=img.width;
var ih=img.height;
var s=Math.min((w/iw),(h/ih))
var c=document.createElement('canvas');
c.width=iw*s;
c.height=ih*s;
c.getContext('2d').drawImage(img,0,0,iw,ih,0,0,iw*s,ih*s);
return(c);
}
// draw the toolbar, image & any clone thumbnails added to the image
function draw(){
ctx.clearRect(0,0,cw,ch);
ctx.fillRect(0,0,cw,tbar.height);
ctx.strokeRect(0,0,cw,tbar.height);
ctx.drawImage(mainImg,0,tbar.height);
for(var i=0;i<icons.length;i++){
var icon=icons[i];
ctx.drawImage(thumbs[icon.index],icon.x,icon.y);
}
for(var i=0;i<dropped.length;i++){
var icon=dropped[i];
ctx.drawImage(thumbs[icon.thumbIndex],icon.x,icon.y);
}
if(dragging){
ctx.drawImage(thumbs[dragging.thumbIndex],dragging.x,dragging.y);
}
}
//
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get the mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// hit test the toolbar icons and any dropped icons
var mx=startX;
var my=startY;
// hit test toolbar icons
if(mx>0 && mx<tw*icons.length && my>2 && my<2+tw){
var icon=icons[parseInt(mx/tw)];
dragging={thumbIndex:icon.index,x:icon.x,y:icon.y};
dragging.source='icons';
return;
}
// hit test dropped icons
for(var i=0;i<dropped.length;i++){
var icon=dropped[i];
if(mx>icon.x && mx<icon.x+tw && my>icon.y && my<icon.y+th){
dragging=dropped[i];
dragging.source='dropped';
dragging.droppedIndex=i;
return;
}
}
}
// Add any
function handleMouseUpOut(e){
if(!dragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
//
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// add the icon to its dropped position
if(dragging.source=='icons'){
if(dragging.y>tbar.height){
dropped.push(dragging);
}
// remove the dropped icon if it's back in the toolbar
}else if(dragging.y<tbar.height){
dropped.splice(dragging.droppedIndex,1);
}
// clear dragging
dragging=null;
// redraw
draw();
}
// move any icon that's being dragged
function handleMouseMove(e){
if(!dragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// move the dragged icon by the distance the mouse
// has moved since the last mousemove event
var dx=mouseX-startX;
var dy=mouseY-startY;
startX=mouseX;
startY=mouseY;
dragging.x+=dx;
dragging.y+=dy;
// redraw
draw();
}
// save just the image with dropped icons
$('#save').click(function(){
var c=document.createElement('canvas');
c.width=cw;
c.height=ch-tbar.height;
c.getContext('2d').drawImage(canvas,0,tbar.height,cw,ch-tbar.height,0,0,c.width,c.height);
var img=new Image();
img.onload=function(){
document.body.appendChild(img);
}
img.src=c.toDataURL();
});
body{ background-color: ivory; }
#canvas{border:1px solid lightgray; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id='save'>Save</button>
<h4>Drag from top toolbar & icon will be drop-copied<br>Drag dropped icons around image<br>Drag dropped icon back to toolbar to delete.</h4>
<canvas id="canvas" width=300 height=300></canvas>
It's a little tough to follow, but I suspect you're asking about compositing all of the existing DOM images onto a single canvas context. For that you'll probably want to look at the globalCompositeOperation property on the 2dcanvas:
globalCompositeOperation - the property
Compositing Tutorial - basic outline of how the different types work
Also your 3rd question: $('#foto') returns a jquery object, and document.getElementById returns a DOM object. To get the actual DOM element you would do something like: var theEl = $('#foto')[0]
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();
$(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.
I've got a problem filling 25 canvas elements automatically in a for loop. They are numbered like so: can01 to can25.
I've tried all I knew to draw different images on the canvas and I have spent a lot of time in searching a few articles which are about this problem but I haven't found any.
This is my working code to fill all canvas elements with the same image:
var imageGrass = new Image();
imageGrass.src = 'recources/imagesBG/grass.jpg';
imageGrass.onload = function() {
for (var i = 1; i < 26; i++)
{
if( i < 10 )
{
var task = "can0" + i + "_ctx.drawImage(imageGrass, 0, 0);";
eval(task);
}
else
{
var task = "can" + i + "_ctx.drawImage(imageGrass, 0, 0);";
eval(task);
}
}
}
But I really don't know how to make the imageGrass.src dynamic. For example, the canvas element no. 5 (can05) in this case shall look like stone texture.
I´m really looking forward to read your ideas. I just don't get it.
Here’s how to impliment Dave’s good idea of using arrays to organize your canvases:
Create an array that will hold references to all your 25 canvases (do the same for 25 contexts)
var canvases=[];
var contexts=[];
Next, fill the array with all your canvases and contexts:
for(var i=0;i<25;i++){
var canvas=document.getElementById("can"+(i<10?"0":""));
var context=canvas.getContext("2d");
canvases[i]=canvas;
contexts[i]=context;
}
If you haven't seen it before: i<10?"0":"" is an inline if/else used here to add a leading zero to your lower-numbered canvases.
Then you can fetch your “can05” canvas like this:
var canvas=canvases[4];
Why 4 and not 5? Arrays are zero based, so canvases[0] holds can01. Therefore array element 4 contains your 5th canvas “can05”.
So you can fetch the drawing context for your “can05” like this:
var context=contexts[4];
As Dave says, “evals are evil” so here’s how to fetch the context for “can05” and draw the stone image on it.
var context=contexts[4];
context.drawImage(stoneImage,0,0);
This stone drawing can be shortened to:
contexts[4].drawImage(stoneImage,0,0);
You can even put this shortened code into a function for easy reuse and modification:
function reImage( canvasIndex, newImage ){
contexts[ canvasIndex ].drawImage( newImage,0,0 );
}
Then you can change the image on any of your canvases by calling the function:
reimage( 4,stoneImage );
That’s it!
The evil-evals have been vanquished (warning: never invite them to your computer again!)
Here is example code and a Fiddle: http://jsfiddle.net/m1erickson/ZuU2e/
This code creates 25 canvases dynamically rather than hard-coding 25 html canvas elements.
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:0px; margin:0px;border:0px; }
canvas{vertical-align: top; }
</style>
<script>
$(function(){
var canvases=[];
var contexts=[];
var grass=new Image();
grass.onload=function(){
// the grass is loaded
// now make 25 canvases and fill them with grass
// ALSO !!!
// keep track of them in an array
// so we can use them later!
make25CanvasesFilledWithGrass()
// just a test
// fill canvas#3 with gold
draw(3,"gold");
// fill canvas#14 with red
draw(14,"red");
}
//grass.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/grass.jpg";
//grass.src="grass.jpg";
function make25CanvasesFilledWithGrass(){
// get the div that we will fill with 25 canvases
var container=document.getElementById("canvasContainer");
for(var i=0;i<25;i++){
// create a new html canvas element
var canvas=document.createElement("canvas");
// assign the new canvas an id, width and height
canvas.id="can"+(i<10?"0":"")+i;
canvas.width=grass.width;
canvas.height=grass.height;
// get the context for this new canvas
var ctx=canvas.getContext("2d");
// draw the grass image in the new canvas
ctx.drawImage(grass,0,0);
// add this new canvas to the web page
container.appendChild(canvas);
// add this new canvas to the canvases array
canvases[i]=canvas;
// add the context for this new canvas to the contexts array
contexts[i]=ctx;
}
}
// test -- just fill the specified canvas with the specified color
function draw(canvasIndex,newColor){
var canvas=canvases[canvasIndex];
var ctx=contexts[canvasIndex];
ctx.beginPath();
ctx.fillStyle=newColor;
ctx.rect(0,0,canvas.width,canvas.height);
ctx.fill();
}
}); // end $(function(){});
</script>
</head>
<body>
<div id="canvasContainer"></div>
</body>
</html>