Replacing an image in KineticJS - javascript

I'm trying to build a web app using KineticJS, I basically have a function which runs on load which adds an image to a canvas (stage in KineticJS). Each time the function runs though, it is adding the image again underneath the existing one, where as I need it to replace the one that is currently on the stage. I know it's something to do with me creating a new image object each time the function is loaded, but I'm confused as to why it's positioning is different (underneath). I have simulated the same event here: http://jsfiddle.net/UCvbe/ - If you click the button several times, the image duplicates below. The final function will be ran when a user selects a knocker, therefore the final function must accept a parameter eg 'silver' so that it knows to draw the silver knocker etc.
Any help will be greatly appreciated!

Your code looks like this:
$('#testBtn').click(function() {
var stageKnocker = new Kinetic.Stage({
container: "canvas-overlay",
width: 402,
height: 470
});
var layerKnocker = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function(){
var image = new Kinetic.Image({
x: 193,
y: 110,
image: imageObj,
width: 25,
height: 50,
draggable: true
});
layerKnocker.add(image);
stageKnocker.add(layerKnocker);
};
imageObj.src = "http://shop.windowrepairshop.co.uk/WebRoot/Store3/Shops/es115683_shop/49E6/ECF2/0C60/3750/10B1/50ED/8970/710D/V6GP_0020_gold_0020_plate_0020_vic_0020_urn_0020_LArge.jpg";
})
The first problem is that you are creating a new Stage each time a user clicks the button. Don't do that--you only want one stage.
You're also creating a new layer each time a user clicks the button. Don't do that. You only need & want a single layer.
Finally, you are creating a new Kinetic.Image each time a user clicks the button and adding it to the layer. That's fine! Keep doing that. But you also want to remove the old Kinetic.Image. If you don't, you will just keep adding images, not replacing them.
So before adding the Kinetic.Image to the layer, you want to remove any existing images from the layer: layerKnocker.removeChildren() will remove everything in the layer.
When you're done, you will have a single stage, a single layer, and at any point in time a single image.

Related

How to add multiple image to canvas using fabricjs

I have read several questions/answers about this topic, but I can't seem to find my solution. So sorry if you guys find this question duplicated.
Here is my situation;
Let's say I have a menu for user to click on it and it will display the image of that selected item on canvas, also user can resize/drag/drop that image as they want. Right now I can make it works only one time with this code
addAction(selectedAction: any) {
var canvas = new fabric.Canvas('c');
var imgObj = new Image();
imgObj.src = selectedAction.image;
imgObj.onload = function() {
var imgInstance = new fabric.Image(imgObj, {
left: 200,
top: 200,
});
canvas.add(imgInstance);
}
The problem is when user click another item in the menu, the previous item that was on the canvas disappear, instead the current item display on the canvas. But if I click on that image, it will disappear and the previous image display on the canvas.
I want them BOTH stay on the canvas at the same time. Please advice.
Real quick answer: http://jsfiddle.net/8yf15nqp/1/.
var canvas = ...// setup canvas
function addImage (url) {
fabric.Image.fromURL(url, function (img) {
// deal with image
canvas.add(img);
});
}
Setup a function to handle adding and then call it when you want to add a new image. I'd assume you'll want to pass the url to the function and do other stuff, but this is a pretty stripped down example.

Using Fabric.js, when an image is scaled, how do you replace it with another image?

I'm building a new version of my online t-shirt designer using FabricJS to replace an existing Flash one that I created several years ago.
I've run into a problem that I need help with. All images that I work with are raster images. When I scale an image or make changes to it such as recoloring it, I make a call to the server to create the new image and then I need to be able to replace the existing image on the canvas.
The only way I've been able to figure out how to do it is by using the canvas method getActiveObject(). This works fine, but if you have multiple objects selected, you don't know which one to update.
When the image is originally added to the canvas, I'm attaching an onModified event handler to it. Only when the scale of the image changes, do I call the server side script to generate the new image. The code I'm currently using is listed below. Any help would be greatly appreciated.
Thanks
fabric.Image.fromURL(previewPath, function (img) {
var sprite = img.set({ left: leftX, top: topY, angle: angle, borderColor: 'black', cornerColor: 'red', cornerSize: 6, transparentCorners: false });
$scope.canvas.add(sprite);
$scope.canvas.renderAll();
sprite.on('modified', function () {
// Modified event is only executed for scaling
if (1 != sprite.scaleX.toString() || 1 != sprite.scaleY.toString()) {
fabric.Image.fromURL(previewPath, function (img) {
var newSprite = img.set({ left: leftX, top: topY, angle: angle, width: width, height: height });
// Replaces visible object on canvas
// Since new image is replacing old one, need to set the scale back to 1
sprite.scale(1);
$scope.canvas.renderAll();
});
}
});
Not sure if I've read it correctly, but wouldn't you be able to add an extra property to the fabric object when it is created indicating what it is and then use that to figure out which object to remove?
e.g. in the above, you'd do sprite.objectName = "tshirtSleeves", then when you get new t-shirt sleeves, get the fabric object with objectName = "tshirtSleeves" and remove that.
You could loop through all of the objects on the canvas, check that it's an image, and then make your image changes there as needed.
This option of course has it's possible efficiency issues if you have a lot of non-image objects on the canvas. But it's an option.
You do need to do a renderAll after this of course.
var objects = document.getElementById('canvasId').fabric._objects;
jQuery.each( objects, function( key, eachObject ) {
if( eachObject.type == "image" ) {
//Perform scale and image replacement as needed here
}
});
$scope.canvas.renderAll();
Actually turned out to be pretty simple. Just pass the new image url to the setSrc method of the image object as below.
sprite.setSrc(previewPath, function (img) {
sprite.scale(1);
$scope.canvas.renderAll();
});

Clickable outline around Snap SVG path

Currently I create a small modeling tool using Snap SVG.
var p = Snap.path(pathString).attr({fill:'none', stroke:'black', strokeWidth:1});
creates connections between other elements.
Every element, including the paths are clickable. Since it is hard to click exactly the path I tried to create an invisible outline around the path using Snap.filter.shadow so that a click event could be triggered for the path:
var filter = this.paper.filter(Snap.filter.shadow(2,2,1));
filter.click(function() {
do_as_if_i_clicked_the_path();
});
p.attr('filter', filter);
But adding a click event to the filter doesn't work.
Is there any way to create an invisible outline that is connected to the path so I can add events on it?
One possibility that works quite well, is to clone the object you want to have an event on, and either scale it up, or make the strokeWidth larger, and then have a very low opacity (you can increase this just for testing and then reduce it to almost invisible).
If you add it to a group like this example, you can just put a drag on the group, so no need to remove it or anything (unless you need the clone not to be there sometimes, which makes sense).
jsfiddle
var s = Snap("#svg");
var myLine = s.path('M0,0L100,100').attr({ stroke: 'black', strokeWidth: 1 });
var myClone = myLine.clone().attr({ strokeWidth: 50, opacity: 0.01 });
var myGroup = s.g( myLine, myClone );
myGroup.drag();

Bring Canvas Instance to Front

I am currently working with the awesome library Fabric.js which has thus far met all of my needs until now. I am attempting to create a layering system by adding new instances of fabric.Canvas each with their own objects/properties of course.
I got this idea from Kangax:
https://twitter.com/fabricjs/status/344822078228283392
I am having trouble switching between these layers (canvas instances).
For example, how would I bring one to the front?
// Create two instances of fabric.Cavnas
var canvas1 = new fabric.Canvas('canvas');
var canvas2 = new fabric.Canvas('canvas');
// On button click, bring canvas 2 to the front
$( "#switchbutton" ).click(function() {
// Attempts:
//fabric.Canvas.bringToFront(canvas2);
//fabric.Object.opacity(0);
fabric.Object.bringToFront(canvas2);
//canvas1.bringToFront(canvas2);
});
Method 1
As I understand you can't do that. You are trying to bring a object to front in canvas. You need to change z-index of canvas to bring it to front
Basically You need to have many different canvas elements and initialize fabricjs as many layers u have.
So for each new layer your create new canvas element and initialize fabricjs
var canvas2 = new fabric.Canvas('canvas2');
var canvas3 = new fabric.Canvas('canvas3');
And then u will have too keep track of your canvas layers and adjust z-index of your layer canvas and upper canvas
Method 1 (preferable)
Another option would be to keep one canvas and add custom attribute to your obejects "layerId".
So when u create a object :
canvas.add(new fabric.Circle({ layerId: activeLayerId, radius: 30, fill: '#f55', top: 100, left: 100 }));
And when you switch layers u just disable other layer objects
canvas.forEachObject(function(obj) {
obj.selectable = obj.layerId == activeLayerId ? true : false;
});
Or if u want to bring to front layer objects
canvas.forEachObject(function(obj) {
if(obj.layerId == activeLayerId) {
obj.bringForward();
}
});

HTML image onmouseover event not firing

I have an image that's being displayed on an HTML5 canvas, to which I have added an onmouseover event that theoretically should call a JavaScript function that has been written in the same file.
However, when I view my page, and hover the cursor over the image, nothing happens.
I've added the image to a hidden section of my HTML using the line:
<img id="assetBox" src = "images/box.png" alt="Assets" onmouseover = "displayAssetDescriptionTip()"/>
The image has then been drawn to the canvas using the function:
function drawDescriptionBoxes(){
var assetsDescriptionBox = document.getElementById("assetBox");
var liabilitiesDescriptionBox = document.getElementById("liabilitiesBox");
var incomeDescriptionBox = document.getElementById("incomeBox");
var expenditureDescriptionBox = document.getElementById("expenditureBox");
assetsDescriptionBox.src = 'images/box.png';
liabilitiesDescriptionBox.src = 'images/box.png';
incomeDescriptionBox.src = 'images/box.png';
expenditureDescriptionBox.src = 'images/box.png';
context.drawImage(assetsDescriptionBox, 70, 400, 120, 70);
context.drawImage(liabilitiesDescriptionBox, 300, 400, 120, 70);
context.drawImage(incomeDescriptionBox, 530, 400, 120, 70);
context.drawImage(expenditureDescriptionBox, 760, 400, 120, 70);
context.strokeText("Assets", 100, 490);
context.strokeText("Liabilities", 325, 490);
context.strokeText("Income", 550, 490);
context.strokeText("Expenditure", 775, 490);
}
and the function that I want to be called when the cursor is detected as hovering over the image is:
function displayAssetDescriptionTip(){
document.getElementById('tipsParagraph').innerHTML = "Assets are items that can be bought or sold for cash.";
console.log("displayAssetDescriptionTip being called");
}
But, for some reason, when I view my page in the browser, and hover the cursor over the image on the canvas, nothing happens, and nothing is displayed in the console... I assume that this means that my onmouseover event isn't firing, but I have no idea why this is- can anyone explain it to me, and point out what I need to do to get it right?
Edit 22/02/2013 # 14:40
I tried adding the following JS to keep track of the mouse coordinates in one of the separate JS files I have:
/*Add code to keep track of the mouse coordinates */
var boundingBox = canvas.getBoundingClientRect();
var mousex = (mouse_event.clientX-boundingBox.left)*(canvas.width/boundingBox.width);
var mouseY = (mouse_event.clientY-boundingBox.top)*(canvas.height/boundingBox.height);
I know I will have to write the code to tell the script what to actually do with the coordinates, but I just wanted to give this a try first, since it's been a while since I've done any work with coordinates myself. But when I view the page in a browser, I'm getting a console error that says that "canvas is not defined, and complains about the line:
var boundingBox = canvas.getBoundingClientRect();
I'm wondering if this is because this is a separate script file, and doesn't actually have any reference to the canvas I'm using until where I've just added it. Do I need to define it again, even though it's already been defined in the HTML page that this script is being run on?
Just drawing an image doesn't magically make the image behave like a node. For instance, you can't change its src and have it automatically change the image shown on the canvas.
Instead, you have to manually track mouse movement with onmousemove on the canvas, then compute what the mouse is hovering over and do something about it. Having had experience making menus in Game Maker, I can tell you it's hard as hell!
This is an assumption, but the image that is drawn is a different element then the one that you have the onmouseover on. Try adding a class to the image and the one being drawn and set the onmouseover on the class (check jQuery to make this process easier)

Categories

Resources