KineticJS - How to Change Image src on Button Click - html

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...

Related

Capturing and saving objects coordinates with fabric js

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.

How to dynamically change images using Metaio

I have just start learning Metaio. I am working on a simple development test project.
I have so far made the tracking sheet, put image and two arrows (buttons) which means next image and previous image.
For testing I made one of the buttons to display the image and the other for hiding the image. All this works fine so far.
My question is when I added extra images how can I shift the images dynamically back and forward using my next and previous buttons?
My testing code:
button2.onTouchStarted = function () {
image1.hide();
};
button1.onTouchStarted = function () {
image1.display();
};
It can be done different ways, I suggest you to use arel.Scene.getObject and put your image names inside array and each time you click next or previous you count the array key up or down.
I assume you are using Metaio Creator editor.
You have to added codes 3 different places:
Previous (left arrow button)
button1.onTouchStarted = function () {
if (currentImageIndex == firstImageIndex) {
return;
}
arel.Scene.getObject(imageArray[currentImageIndex]).hide();
currentImageIndex--;
arel.Scene.getObject(imageArray[currentImageIndex]).display();
globalsVar['currentImageIndex'] = currentImageIndex;
};
Next (right arrow button)
button2.onTouchStarted = function () {
if (currentImageIndex == lastImageIndex) {
return;
}
arel.Scene.getObject(imageArray[currentImageIndex]).hide();
currentImageIndex++;
arel.Scene.getObject(imageArray[currentImageIndex]).display();
globalsVar['currentImageIndex'] = currentImageIndex;
};
On your Global Script
var imageArray = ["image1", "image2"]; // and so on extra image names
var firstImageIndex = 0;
var lastImageIndex = imageArray.length - 1;
var currentImageIndex = firstImageIndex;
globalsVar = {};
arel.Scene.getObject(imageArray[currentImageIndex]).display();

Bring GoogleMaps InfoWindow to front

I have a GoogleMaps APIv3 application in which multiple InfoWindows can be open at any one time. I would like to be able to bring an obscured InfoWindow to the front of all other InfoWindows if any part of it is clicked - similar to the behaviour of windows in MS Windows OS.
I had thought to add an onclick event handler which increases the z-index of the InfoWindow, but the event handler does not appear to be firing.
ZIndex is a global variable that keeps increasing as InfoWindows are clicked - or thats the theory anyway.
Can anyone help ?
Here is my code:-
var ZIndex=1;
var iw = new google.maps.InfoWindow({ content:contentString });
google.maps.event.addListener(iw, 'click', handleInfoWindowClick(iw) );
function handleInfoWindowClick(infoWindow) {
return function() {
infoWindow.setZIndex(ZIndex++);
}
}
there is no click-event for an infoWindow, it's a little bit more difficult.
you'll need to use an element(not a string) as content for the infowindow, because you need a DOMListener instead a listener for the infowindow-object
when domready-fires, you must apply the click-DOMListener to the anchestor of this content-node that defines the infowindow
The following code will do this for you, add this to your page:
google.maps.InfoWindowZ=function(opts){
var GM = google.maps,
GE = GM.event,
iw = new GM.InfoWindow(),
ce;
if(!GM.InfoWindowZZ){
GM.InfoWindowZZ=Number(GM.Marker.MAX_ZINDEX);
}
GE.addListener(iw,'content_changed',function(){
if(typeof this.getContent()=='string'){
var n=document.createElement('div');
n.innerHTML=this.getContent();
this.setContent(n);
return;
}
GE.addListener(this,'domready',
function(){
var _this=this;
_this.setZIndex(++GM.InfoWindowZZ);
if(ce){
GM.event.removeListener(ce);
}
ce=GE.addDomListener(this.getContent().parentNode
.parentNode.parentNode,'click',
function(){
_this.setZIndex(++GM.InfoWindowZZ);
});
})
});
if(opts)iw.setOptions(opts);
return iw;
}
Instead of google.maps.InfoWindow() you must call now google.maps.InfoWindowZ()
It also returns a genuine InfoWindow, but with the mentioned listener applied to it. It also creates the node from the content when needed.
Demo: http://jsfiddle.net/doktormolle/tRwnE/
Updated version for visualRefresh(using mouseover instead of click) http://jsfiddle.net/doktormolle/uuLBb/

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.

How to create a text overlay in Google Maps API v3 that does not pan?

I'm using Google Maps API v3. I would like to create a text overlay on a map that does not move when the map is panned. Is the best approach to manipulate the DOM elements accessible from the MapPanes object or is it best to create a custom control even though it would not do much other than display text?
The simplest way that I found worked for me was a few lines of JavaScript added after I created a new map. So, after this:
map = new google.maps.Map('myMapDivId', mapOptions);
add this:
var myTitle = document.createElement('h1');
myTitle.style.color = 'white';
myTitle.innerHTML = 'Hello World';
var myTextDiv = document.createElement('div');
myTextDiv.appendChild(myTitle);
map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(myTextDiv);
You will probably want to style the text to look nicer.
An alternative is to put the div in your HTML:
<div id="myTextDiv" style="color: white; position: absolute;">
<h1>Hello World</h1>
</div>
and then do this in your JavaScript:
var myControl = document.getElementById('myTextDiv');
map.controls[google.maps.ControlPosition.TOP_CENTER].push(myControl);
NOTE an important difference: If you use the HTML route to define your div, you must set the position style to absolute in the HTML to avoid rendering problems.
From you're describing, the best approach would be a custom control. Docs for that are here. Custom controls can be as simple or a complicated as you want.
One reason why you would want to muck around with the map panes is if you wanted such a 'control' to lie underneath the markers / shadows / polylines etc. I'm doing this right now to show a crosshairs in the center of the map at all times. But because I keep it as an overlay, I choose the panes in such a way that the markers are above it, so they can continue to be clicked and interacted with - using the mapPane. Here's how I'm doing it:
var CrosshairOverlay = function(map){
this._holder = null;
this.setMap(map);
};
CrosshairOverlay.prototype = new google.maps.OverlayView();
CrosshairOverlay.prototype.onAdd = function(){
var map = this.getMap();
var holder = this._holder = $('<div>').attr('id', 'crosshair')[0];
var crosshairPaper = this._paper = R(holder, 150, 150);
// ... all your drawing and rendering code here.
var projection = this.getProjection();
var wrappedHolder = $(holder);
var updateCrosshairPosition = function(){
var center = projection.fromLatLngToDivPixel(map.getCenter());
wrappedHolder.css({left:center.x-75, top:center.y-75});
}
_.each(['drag','dragend','bounds_changed','center_changed','zoom_changed','idle','resize'], function(event){
google.maps.event.addListener(map, event, updateCrosshairPosition);
});
google.maps.event.addListener(map, 'maptypeid_changed', function(){
_.defer(updateCrosshairPosition);
});
this.getPanes().mapPane.appendChild(holder);
};
CrosshairOverlay.prototype.draw = function(){
};
CrosshairOverlay.prototype.onRemove = function(){
this._holder.parentNode.removeChild(this._holder);
this._holder = null;
};
The reason the maptypeid_changed has its own handler with a defer is because that event is fired before the map properly sets itself up when changing the type. Just run your function after the current event loop.