Images and layers (HTML5, canvas, JS, CSS ??) - javascript

I have two layers one on another top. First layers is background, second layer is flower. How I can know on what layer clicked.
If I clicked on background result it's good return first layer place where clicked:
If I clicked on flower result it's good return second layer place where clicked:
If I clicked on background near flower result it's bad return second layer place where clicked:
3.1 I get second layer because actually image is bigger, because image have transparent place:
For test use JSFiddle: http://jsfiddle.net/sbkhtvmo/1/

You could try creating a transparent SVG path layer that covers the flower and make that the flower click layer, then make everything else the background click layer.

Use clientX and clientY to get the position of the mouse in the window.You can also do that with jQuery offset to get the position.
jQuery example that I found useful on stackoverflow:
$(document).ready(function() {
$('imageElement').click(function(e) {
var offset = $(this).offset();
alert(e.clientX - offset.left);
alert(e.clientY - offset.top);
});
});

Related

In Leaflet, how to load tiles only when clicking on each?

I have a leaflet map consisting of a satellite image with lat-long metadata. My objective is to display a rectangle highlighting the selected tile on mouseclick and fetch the corresponding tile. The L.rectangle method needs bounds to display the rectangle. In order to calculate the SE and NW corners, I use the following function that I found at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_numbers_to_lon..2Flat.
This is for SE and the same function is changed for getting NW as seen in the above link. These are used as bounds for the rectangle.
//I have a fixed zoom level for my problem :4
function(xtile,ytile,zoom) //here i am giving the tile location coordinates as the input
{
n=2.0**zoom;
lon= (xtile+1)/n*360-180;
lat_rad=Math.atan(Math.Sinh(Math.PI*(1-2*(ytile+1)/n)));
lat=lat_rad(180/Math.PI);
return[-lat,lon]
}
The output comes out to be as follows:
NW -782.67,-112.4
SE -79.163,-89.9
with(suppose) xtile=5 and ytile=14
The actual output however should have been:
NW -66.51,-45
SE -74.01,-22.5
and the corresponding(again,suppose) xtile=5 and ytile=1
So basically, for some tiles , the rectangle shows just fine. But for others, it jumps to some other tile. I have a display window for the selected tileand when the rectangle jumps, neither the selected tile nor the highlighted tile is fetched. I will be really thankful is someone can help me with this.
My objective is to display a rectangle highlighting the selected tile on mouseclick and fetch the corresponding tile.
Then a good approach to the root problem is to use a custom L.GridLayer, since it provides functionality to create empty tiles, attach events to them and handle tile coordinates without the need to handle bounding boxes explicitly.
It would go like this:
L.GridLayer.ClickyLoad = L.GridLayer.extend({
// By default, the container for a whole zoom level worth of visible tiles
// has a "pointer-events: none" CSS property. Override this whenever a new
// level container is created. This is needed for pointer (mouse) interaction.
_onCreateLevel: function(level) {
level.el.style.pointerEvents = 'inherit';
},
// The tiles shall be empty <div>s with some DOM events attached.
createTile: function(coords){
var tile = L.DomUtil.create('div');
tile.style.border = '1px solid black';
// Highlighting the tile on mouse hover is just swag.
L.DomEvent.on(tile, 'mouseover', function(){
tile.style.border = '2px solid red';
});
L.DomEvent.on(tile, 'mouseout', function(){
tile.style.border = '1px solid black';
});
// When a tile is clicked, calculate the URL of the tile (using the same
// logic as L.TileLayer.getTileUrl() ), create a <img> element, and put
// the newly created image inside the empty <div> tile.
L.DomEvent.on(tile, 'click', function(){
var img = L.DomUtil.create('img');
img.src = L.Util.template('https://tile.openstreetmap.org/{z}/{x}/{y}.png', coords);
tile.appendChild(img);
});
return tile;
}
});
// Create instance and add to map
(new L.GridLayer.ClickyLoad()).addTo(map);
See a fully working example here.
As usual with this kind of non-trivial-looking examples, reading through the Leaflet documentation, the tutorial about extending classes, and the Leaflet source code itself is recommended.

Moving multiple objects simultaneously in createJS/easelJS

I've been using easelJS within the createJS framework for a project and have enjoyed it a lot until recently hitting a roadblock. I have multiple objects that I'd like to move simultaneously when one of the group is dragged. Here is my current situation:
What I'd like to do is when the red circle is moved, the red crosshairs would also move so that they appear to be "locked" to the circle. The same with the green circle.
I have been able to accomplish something very close to this by adding the circles and crosshairs to a container, as mentioned in the answers to this question:
Easeljs Scrollable Container
But the issue I encounter is that the container is actually a rectangle, such that I can click anywhere between the circle and crosshairs to move the various objects contained within the container. Instead I would like for the objects to be moved only when I click on a circle.
Does anyone have any idea how to accomplish this? Am I correct in thinking this can be accomplished somehow with easelJS containers?
Containers should be fine. You can turn off mouseEnabled on the cross-hair in order to make it ignore the mouse.
You could also just store the offset for each cross-hair/circle, and just set the cross-hair position when the circle moves.
Here is a quick demo:
http://jsfiddle.net/lannymcnie/kah9of6e/
// Set the offset when the circle is pressed
circle.on("mousedown", function(e) {
circle.offset = new createjs.Point(crosshair.x-circle.x, crosshair.y-circle.y);
});
// Add drag and drop to each shape
circle.on("pressmove", handleDrag);
crosshair.on("pressmove", handleDrag);
function handleDrag(e) {
// Move the target to the mouse
e.target.x = e.stageX; e.target.y = e.stageY;
// If the target is the circle, also move the cross-hair
if (e.target == circle) {
// Move the cross-hair
crosshair.x = circle.x + circle.offset.x;
x.y = circle.y + circle.offset.y;
}
}

Circular canvas corners clickable in Chrome

I have two canvases. I have made them circular using border-radius. The 2nd is positioned inside the first one (using absolute position).
I have click events on both circles. If you click on inside canvas, the color at the point of the click is loaded in the outside canvas with opacity varying from white to the picked color and finally to black. If you click on outer canvas the exact color value at that point is loaded in the text-box at the bottom
I am unable to click in red zones (as shown in figure below) of the outer canvas when using chrome. I tried z-idex, arcs but nothing is helping me. But In Firefox everything is working fine.
Note: You can drag the picker object in the outer circle. But if you leave it in red zones, you would not be able to click it again in Chrome. Clicking in green zone will get you its control again
Code in this JSFiddle
Edit
I excluded all irrelevant code to make it easy. Now there is only a container having two canvas.
Filled simply with two distinct colors. Open following fiddle link in both chrome and firefox. Click on both cirles in different zones and see difference in chrome and firefox. I want them to behave in chrome as they do in firefox
Note I will ultimately draw an image in inner canvas.
Updated Fiddle Link
-
Your problem is because canvases currently are always rectangular, even if they don't look rectangular. Border radius makes the edges except the circle transparent, but it still doesn't stop events in Chrome on the corner areas. This is why you cannot click the bottom circle in those areas
I even tried putting it inside of a container that had a border-radius instead but the click event still goes through
With that being said, you have two options. You could either change your code to only use one canvas with the same type of layout, just drawing the background circle before the other each time. Essentially you'd draw a circle, draw your black to color to white gradient, use the xor operation to combine the two into one circle, then do the same with the rainbox gradient. You must draw the background circle first because canvas paints over the old layers every time
or
You could use javascript to only detect clicks in the circular area which takes just a little bit of math (: This solution is featured in edit below
In the future, CSS Shapes may allow canvases to be non-rectangular elements to be used, I'm actually not sure, but we don't have that capability yet at least
Edit
Alright, so after going through your code a bit it seems there are some things I should cover before I offer a solution
Setup all your finite variables outside of the functions that run every time. This means you don't put them (like radiuses, offsets, etc.) in the click function or something that runs often since they don't change
Your "radius"es are actually "diameter"s. The format of .rect goes .rect(x, y, width (diameter of circle), height (diameter of circle))
Almost always when overlaying canvases like you are you want to make them equal dimensions and starting position to prevent calculation error. In the end it makes it easier, doing all relative positioning with javascript instead of mixing it with CSS. In this case, however, since you're using border-radius instead of arc to make a circle, keep it like it is but position it using javascript ....
jQuery isn't needed for something this simple. If you're worried about any load speed I'd recommend doing it in vanilla javascript, essentially just changing the .click() functions into .onclick functions, but I left jQuery for now
You can declare multiple variables in a row without declaring var each time by using the following format:
var name1 = value1,
name2 = value2;
Variables with the same value you can declare like so:
var name1 = name2 = sameValue;
When children have position:absolute and you want it to be positioned relative to the parent, the parent can have position:relative, position:fixed, or position:absolute. I would think you'd want position:relative in this case
When you don't declare var for a variable it becomes global (unlessed chained with a comma like above). For more on that read this question
Now, onto the solution.
After talking with a friend I realized I could sort do the math calculation a lot easier than I originally thought. We can just calculate the center of the circles and use their radiuses and some if statements to make sure the clicks are in the bounds.
Here's the demo
After everything is set up correctly, you can use the following to detect whether or not it's in the bounds of each
function clickHandler(e, r) {
var ex = e.pageX,
ey = e.pageY,
// Distance from click to center
l = Math.sqrt(Math.pow(cx - ex, 2) + Math.pow(cy - ey, 2));
if(l > r) { // If the distance is greater than the radius
if(r === LARGE_RADIUS) { // Outside of the large
// Do nothing
} else { // The corner area you were having a problem with
clickHandler(e, LARGE_RADIUS);
}
} else {
if(r === LARGE_RADIUS) { // Inside the large cirle
alert('Outer canvas clicked x:' + ex + ',y:' + ey);
} else { // Inside the small circle
alert('Inner canvas clicked x:' + ex + ',y:' + ey);
}
}
}
// Just call the function with the appropriate radius on click
$(img_canvas).click(function(e) { clickHandler(e, SMALL_RADIUS); });
$(wheel_canvas).click(function(e) { clickHandler(e, LARGE_RADIUS); });
Hopefully the comments above and code make enough sense, I tried to clean it up as best as I could. If you have any questions don't hesitate to ask!

Reloading an image after zooming in KineticJS

I have a group, inside of which is an image. I've got code working so you can zoom with the mouse wheel. When the zooming stops, I reload the image, in a larger size. I add the new image to the group ( the old image is still part of the group ) and then I do this:
img.remove();
img.destroy();
imgNew.moveToTop();
imgNew.offsetX = offset.x;
imgNew.offsetY = offset.y;
At the end of this code, my old image disappears and the new one is not visible. It has a position that is sane, and it is the child of my group. It has exactly the size I expect it to have ( it's absolute position is 0,0 and it's size is bigger than my canvas ). I've turned off clipping, so if it was visible anywhere, I'd see it. I've changed the position in the debugger, and called draw() on the canvas, the layer and the group. I have dragging code and I've dragged the drag control ( which is still there ) every where I can. I've also changed my code to just add the new image to the top level layer instead of the lower down group.
I should mention in case it's relevant, the image is coming from a WebAPI RESTful service, and the size is passed in, so the image URLs are different for the two images.
I simply cannot find my image !!! What should I do next ?
This:
KineticJS - How to Change Image src on Button Click
answered my question. Short version: there's a setImage method so you can load a new image, call setImage() and it gets replaced.

Tiles not always clicked first time

I've got an isometric map with selectable tiles. Where the tiles become "highlighted" when they are clicked on, and return to normal when a different tile is clicked. This is done by changing the background image to one of a highlighted looking tile.
However, I noticed that the tiles do not always recognize that they were clicked on the first click. For example, when first starting, clicking on the tile closest to the screen will usually not do anything. Clicking on it again or even three times will finally make it change color, but this multiple click requirement is still present when trying a different tile. Eventually the tiles will change after one click, OR if the mouse press is held down for a second on the tile before releasing.
In the example below, you can see my tile click event handler. Simply search for "TODO" in the JS code.
The problem is likely because there are two tile click events. Can this problem be fixed?
http://jsfiddle.net/briz/jdhPW/8/
I recommend you test out clicking on the first row of tiles. The tiles have the transparent pixels around them, so it's easy to think you're clicking a tile in the middle when your mouse is still registering the tile around it. You can see which tile is currently "active" by it becoming slightly transparent when hovered over.
EDIT I found a solution! I can register the tiles beforehand by calling it like this:
$.fn.gameMap.tileInfo = function(tile,x,y)
{
// Registers each tile with their unique id
tile.id = obj.attr('id') + '_tile_' + x + '_' + y;
// When the tile gets clicked
tile.click(function()
{
.....
}
Then there is only a single instance of the click event, and it works on the first click every time
I went and cleaned up the code a bit using more jQuery than in your original.
$.fn.gameMap.tileInfo = function(tile,x,y) {
// When the tile gets clicked
tile.click(function() {
// When the tile gets clicked again?
var regularTile = "url(http://i244.photobucket.com/albums/gg10/brizthewhitedragon/WorkTilesWatermark.png)";
$(".content .tile").css("background-image", regularTile);
// Makes the tile apparently selected
var selectedTile = "url(http://i244.photobucket.com/albums/gg10/brizthewhitedragon/WorkTilesSelected.png)";
tile.css("background-image", selectedTile);
});
};
Even better would be to simply add a click event to the tiles like this since it binds them all at once (although you would have to restructure your code a bit...):
var tiles = $(".content .tile").click(function() {
// When the tile gets clicked again?
var regularTile = "url(http://i244.photobucket.com/albums/gg10/brizthewhitedragon/WorkTilesWatermark.png)";
tiles.css("background-image", regularTile);
// Makes the tile apparently selected
var selectedTile = "url(http://i244.photobucket.com/albums/gg10/brizthewhitedragon/WorkTilesSelected.png)";
tile.css("background-image", selectedTile);
});

Categories