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);
});
Related
I am trying to add a hover effect to each speech bubble in the image below that link to a different page when clicked on.
Currently, I am using an image map and whenever an area of is hovered over jQuery changes the whole image to be the appropriate image that has the speech bubble filled in. It kind of works but IE flickers every time a hover happens and the website is responsive so the image map does not scale when the screen size is changed. I've tried using https://github.com/stowball/jQuery-rwdImageMaps too but it doesn't seem to work on a mobile device.
Ideally I'd like to be able to have the speech bubbles be separate images that are positioned correctly when scaled so I can manipulate the speech bubbles easier.
Screenshot
You could go with approach which uses scaled coordinates.
Here's the basic idea:
var coordManager = {
"imageBaseHeight":800,
"imageBaseWidth":800,
"imageID":"myImg",
"baseCoordinateActions" = [
{"x":10,"y":10,"h":100,"w":100, "text": "Mousing over first option"}, // the square you want the mouseover to cover for a given action
{"x":200,"y":200,"h":100,"w":100, "text": "Mousing over first option"}
],
"scaledCoordinateActions" : [], // this should contain the baseCoordinateActions with the scaled values
"init" : function(id, baseHeight, baseWidth)
{
var self=this;
this.imageID=id;
this.imageBaseHeight=baseHeight;
this.imageBaseWith=baseWidth;
var img = document.getElementById(id);
img.onresize =function()
{
// regenerate the scaled coordinates based on the difference between the imageBaseHeight and the current height;
// usually you can get the scale by dividing the imageBaseHeight by the actual height
};
image.onmousemove = function(event)
{
// check the mouse location based on scaled coordinates if it's within the scaled coordinates of any of the scaledCoordinateAction items
// display that scaleCoordinateAction item's text using the current eventX and Y, or do it relative to the coordinateACtion X and Y.
// make sure to check if the bubble is already showing before changing the display: property to avoid flicker.
}
}
};
coordManager.init("imageID", 800,800);
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);
});
});
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!
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.
I have an isometric map that has selectable tiles. The map is formed by a matrix such as $('div').gameMap({map:[[{"tile":"tile_0","object":"anObjectOnTile"},{"tile":"tile_3","object":""}], [{...}]],mapsize:3}); If the tile has something within the object:"", then that tile has that object (in my case, an image of an oil derrick) on top of that tile.
There is a Place button which when clicked should place an oil derrick object on top of the currently selected tile.
However, I have run into two problems:
Knowing which tile is currently selected
Adding a single oil derrick
At first I wanted to figure a way to modify the map's matrix to insert the object's name (oilDerrick) into the tile's object parameter, but now I think the better solution would be to append the oilDerrick to the tile. I can't figure out how to append to the particular tile and not every tile.
I've got the code set up to look at and play with: http://jsfiddle.net/briz/jdhPW/13/
There's an oilDerrick object set up in the map's matrix for you to view, which can be removed by deleting the word oilDerrick in the matrix
I played with $(".tile").append($('.oilDerrick')); though I knew that would append to every tile. But I simply do not know enough about appending yet to figure out how to limit it to a single instance.
Each tile has a unique ID. I also tried this in order to add an oilDerrick to a certain tile:
$("#placeButton").click(function()
{
for (var y in config.map)
{
for (var x in config.map[y])
{
document.getElementById(obj.attr('id') + '_tile_' + x + '_' +y).appendChild(".oilDerrick");
}
}
}
But this came to no avail as well. I think I'm heading in the right direction though? Can anyone help?
I did some modifications.
There a little mess in your code. For example, your using jquery but there is a lot of direct DOM manipulation.
.style.background = ...
// should be
.css("background",...)
And the most important:
document.getElementById(...)
// should be
$("#...")
Easier, don't you think? Then, the problem is simple solved by creating a var currentTile and when the user select one tile, it save it. When click in 'place', $(currentTile).append($('.oilDerrick')); And the oil derrick move to where to selected tile.
Anyway, more details you can see in the link.