Capturing and saving objects coordinates with fabric js - html

I am working on an app where a user can create multiple boxes on a canvas, save the coordinates of each box(Top, Left, Width, Height) individually in database, and later retrieve them back on a canvas, whenever needed to update its position.
I am able to do all this, except updating the position of the box.
For eg. Lets say user selects "box1":
1) canvas.on('mouse:down', function (evt) { }); - gives the current coordinates for box1, that matches with the database entry.
2) canvas.on('object:modified', function (evt) { }); - gives the new coordinates for box1, that I can use to update the database fields.
The only problem is, if the user selects box1, drag it some where, drops it, and again drags it to a new position. I loose the original value (since he clicked mouse:down twice for box1), and thus not able to finally identify, which box was updated wrt database.
Below is the code snippet:
window.addRect = function() {
var box = new fabric.Rect({
left: 0,
top: 0,
stroke: 'red',
fill: 'rgba(255,0,0,.4)',
width: 50,
height: 50,
});
box.hasRotatingPoint = false;
canvas.add(box);
}
canvas.on('object:modified', function (evt) { // for updated values
var modifiedObject = evt.target;
console.log('modifiedObject: ' + modifiedObject.get('left'), modifiedObject.get('top'), modifiedObject.getScaledWidth(), modifiedObject.getScaledHeight());
});
canvas.on('mouse:down', function (evt) { // for original values
var downObject = evt.target;
console.log('downObject: ' + downObject.get('left'), downObject.get('top'), downObject.getScaledWidth(), downObject.getScaledHeight()); });
Is there anything other than mouse:down and object:modified, that I can use? I am looking for a solution in Extjs.

Related

Cocos2d-x JS getting variables from scene or layer

I'm currently having issues with the following problem.
I'm trying to access the layer inside the scene and thereby the elements that I set in that layer. In this case I want access to the conv_label in the Layer to set the text.
I'm doing this via a ConversationClass which extends cc.Class.
When I try to access the layer via the variable it doesn't work or with getChildByName or Tag it doesn't work (value is always null).
This is a method inside the ConversationClass, I can console log the currentscene without any problem, but any variable I set doesn't appear in the current scene. in this case the name was "conv_layer", I can access the children by just using array calls, but that's not really a good way it seems and quite confusing.
This I tried:
currentscene.children[0].children[3] will give me the right element.
currentscene.conv_layer.getChildByName("text") says conv_layer does not exist
currentscene.children[0].getChildByName("text") returns null
Does anyone know how to solve this issue or can tell me where my thinking went wrong?
Not sure of it matters, but I call the scene (for now) in the following way.
cc.LoaderScene.preload(conversation_load, function() {
cc.director.runScene(new ConversationScene());
this.startGame();
}, this);
This is where I want access
startConversation: function(conversation) {
this._conversationObject = conversation;
this._currentScene = cc.director.getRunningScene();
console.log(this._currentScene); // Shows current scene object (doesn't have conv_layer property)
if(scene !== null)
this._currentConversationLayer = scene.conv_layer; // Returns null
},
This is my scene:
var ConversationScene = cc.Scene.extend({
conv_layer: null,
onEnter: function() {
this._super();
this.conv_layer = new ConversationLayer();
this.conv_layer.setName('conversation');
this.conv_layer.setTag(1);
this.addChild(this.conv_layer);
}
});
and this is my layer:
var ConversationLayer = cc.Layer.extend({
ctor: function() {
this._super();
this.init();
},
init: function() {
this._super();
var winSize = cc.director.getWinSize();
GD.current_conversation = conversation1;
this.background = new cc.Sprite();
this.background.anchorX = 0.5;
this.background.anchorY = 0.5;
this.background.setPositionX(winSize.width / 2);
this.background.setPositionY(winSize.height / 2);
this.addChild(this.background);
this.girl = new cc.Sprite();
this.girl.anchorX = 0;
this.girl.anchorY = 0;
this.addChild(this.girl);
this.text_background = new cc.Sprite(resources.conversation_interactive_assets, cc.rect(0,0,1920/GD.options.scale,320/GD.options.scale));
this.text_background.anchorX = 0.5;
this.text_background.anchorY = 0;
this.text_background.setPositionX(winSize.width / 2);
this.text_background.setPositionY(0);
this.addChild(this.text_background);
// Left
this.conv_label = new cc.LabelBMFont("", resources.font);
this.conv_label.anchorX = 0;
this.conv_label.anchorY = 0;
this.conv_label.setPositionX((winSize.width - this.text_background.width) / 2 + 20);
this.conv_label.setPositionY(this.text_background.height - 30);
this.conv_label.color = cc.color.BLACK;
this.conv_label.setName('text');
this.addChild(this.conv_label);
}
});
The issue was the loading order of everything.
It seems scenes are loaded in async, so the next function would be called but no layer would exist at that point.
Solved it by doing creation inside the class itself and calling onSceneEnter.

How to hide the overflow of photos in canvas?

I am trying making a photo frame pattern on canvas. In which I want to put two, three or maybe more photos under an overlay frame. Where few parts of the overlay frame are transparent.
If I upload photo1 into first section of frame its visible into second section also and uploading photo2 into section two is also visible into first section. Depending on which photo is uploaded first or edited at last is overlapping the other photo.
I want to know how to hide the overflow of photo so it should not be visible into other sections. How can I achieve this?
I have done this so far:
canvas.on({
'object:moving': onChange,
'object:scaling': onChange,
'object:rotating': onChange,
});
function onChange(options) {
options.target.setCoords();
canvas.forEachObject(function (obj) {
if (obj === options.target)
return;
if (obj.id != 'cover1' && obj.id != 'cover2')
return;
if (options.target.intersectsWithObject(obj)) {
// Hide this portion
canvas.renderAll();
}
});
}
Kindly provide me the best solution
You need to apply a clipTo() method onto the image.
var canvas;
$(function(){
canvas = window._canvas = new fabric.Canvas('canvas');
var radius = 200;
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function(img) {
img.scale(0.5).set({
left: 250,
top: 250,
angle: -15,
clipTo: function (ctx) {
ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
}
});
canvas.add(img).setActiveObject(img);
});
});
This fiddle show the technique
Fiddle

KineticJS - How to Change Image src on Button Click

I'm trying to change the src of an image in my kineticjs stage on a button click.
I have a draggable image (in this case darth-vader) and a static image on top (in this case monkey). On the click of a button i want to be able to replace the draggable image with a new one (yoda)
JSFiddle can be seen here:
http://jsfiddle.net/SkVJu/33/
I thought the following:
btn.addEventListener("click", function (event) {
mainImage.src = path+'yoda.jpg';
layer.removeChildren();
draw(mainImage,true);
draw(foregroundImage,true);
});
would accomplish it: first by updating the src, then removing all objects and redrawing both again in the correct order.
For some reason though i get 2 yoda images placed on the stage - 1 correctly behind but another above everything else...
Instead of removing all the children and adding them back, you can swap image sources easily by using the KineticJS setImage function and passing in a Javascript Image Object: http://kineticjs.com/docs/Kinetic.Image.html#setImage
I updated your draw function so that it takes an id and name so that we can select the Kinetic Image object later:
// Draw function
function draw(image,drag,id,name){
if (typeof id == 'undefined') id = '';
if (typeof name == 'undefined') name = '';
var img = new Kinetic.Image({
image: image,
draggable: drag,
id: id,
name: name
});
layer.add(img);
layer.draw();
return img;
}
and then here is your update click function:
// Change draggable Image
btn.addEventListener("click", function (event) {
layer.get('#mainImageId')[0].setImage(mainImage2); //get the object with id "mainImageId"
layer.draw();
});
jsfiddle
Also, I moved your foregroundImage load function inside the mainImage onload function so that we make sure the Monkey is added to the stage after the mainImage:
// Define draggable image
var mainImage = new Image();
mainImage.onload = function () {
draw(mainImage,true, 'mainImageId');
// Define foreground image
var foregroundImage = new Image();
foregroundImage.onload = function () {
draw(foregroundImage,false);
};
foregroundImage.src = path+'monkey.png';
};
mainImage.src = path+'darth-vader.jpg';
Wouldn't it be easier to load both images right away, create new Kinetic.Group of them, set one visible and other hidden, than just create function that will hide first and show second on click? Just asking cause i have similar issue to deal with, just that in my case exchange should be done among 5 different icons based on settings parameter that user can change...

Can the default behavior of a Google Maps Event be overridden

I am trying create a restriction on the movement of an editable polygon. I want to prevent the inside path from being pulled outside of outer path. I am using Google Maps google.maps.geometry.poly.containsLocation function to check if the new point is within the outer bounds but I can't find a way to stop the action from happening if it is outside.
I have tried returning false but the polygon still goes to the new location
google.maps.event.addListener(poly.getPaths().getArray()[1], 'set_at', function() {
return keepInside(this, arguments[0], arguments[1]);
});
function keepInside(polygon, index, new_point)
{
outer_edge = new google.maps.Polygon({
paths: polygon.getArray(),
});
is_in = google.maps.geometry.poly.containsLocation(new_point,outer_edge);
if(!is_in)
{
return false;
}
return true;
}
You got pretty close with your original code. Instead of returning false, you could set the point back to it's original position. Also, the second argument passed to the set_at listener is not the new point but the old point. Try this:
google.maps.event.addListener(poly.getPaths().getArray()[1], 'set_at', function(index, oldPoint) {
return keepInside(this, index, this.getAt(index), oldPoint);
});
function keepInside(polygon, index, newPoint, oldPoint)
{
outer_edge = new google.maps.Polygon({
paths: polygon.getArray(),
});
if(!google.maps.geometry.poly.containsLocation(newPoint, outer_edge))
{
polygon.setAt(index, oldPoint);
}
}

OpenLayers - clear 'map' div

I am using OpenLayers to display dynamically loaded images. The code I am using is:
var map;
function init(strURL) {
map = new OpenLayers.Map('map');
var options = { numZoomLevels: 3,
isBaseLayer: true, };
var graphic = new OpenLayers.Layer.Image(
'City Lights',
strURL + "?sc=page",
new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
new OpenLayers.Size(580, 288),
options
);
// graphic.events.on({
// loadstart: function () {
// OpenLayers.Console.log("loadstart");
// },
// loadend: function () {
// OpenLayers.Console.log("loadend");
// }
// });
var jpl_wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
"http://t1.hypercube.telascience.org/cgi-bin/landsat7",
{ layers: "landsat7" }, options);
map.addLayers([graphic, jpl_wms]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.zoomToMaxExtent();
}
I am calling this function on a button click event and passing the strURL (sources of the images) at that time. The result is, with each click a different image is loaded and displayed on the web page but is not clearing the previous image. So I 5 different images on the webpage are shown with 5 clicks and so on.
My javascript knowledge is limited, so my apologies if this is a stupid question. How to stop this behavior? Thanks for any assistance.
Also, I didn't quite understand the lines:
var jpl_wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
"http://t1.hypercube.telascience.org/cgi-bin/landsat7",
{ layers: "landsat7" }, options);
But I know these lines are needed since I'm getting js error if I remove them.
So currently you are calling init(strURL) with each button click? Divide that code into two parts:
On page load, create map and layer objects
On button click, just update URL of existing image layer. Image layer has setUrl(url) method for that: http://dev.openlayers.org/apidocs/files/OpenLayers/Layer/Image-js.html#OpenLayers.Layer.Image.setUrl
Here is sample, that should explain it: http://jsfiddle.net/mEHrN/6/
About var jpl_wms = ... - it creates WMS layer, that should display Landsat imagery (but that URL doesn't seem to work). If you remove it, remember also to remove it from map.addLayers:
map.addLayers([graphic]);
Probably this was reason, why you got JS error.