HTML has supported image maps since time immemorial. You know, this sort of thing
<img src="url/to/your/image.jpg" alt="" usemap="#Map" />
<map name="Map" id="Map">
<area href="#" shape="poly" coords="381,90,386,64,421,32,462,19,501,32,535,62,540,83" />
<area href="#" shape="poly" coords="509,115,511,127,492,132,483,119,487,112" />
<area href="#" shape="poly" coords="425,113,433,108,449,118,444,128,429,128" />
</map>
I have some data in a database that determines the source for an image and defines named sets of area coordinates. This is stored as a string of JSON which parses into an object graph, and knockout renders it all into a web page complete with click handlers that return the correct identifier for each area.
All this works just fine, and I mention it only to provide operational context.
When the user clicks an area, I'd like to toggle its visibility. For example, imagine indicating one or more affected regions for an injury on an anatomical diagram, or the bent bits of the car in an insurance claim app, that sort of thing.
The problem is making an area visible. Areas do not render. So I need to draw over the image. I know it can be done; google maps does it.
I don't know where to start or what to search for. This is the first time I've ever needed to draw directly - normally I supply elements and let the browser render as it will.
So, how about some search terms, key words and perhaps even links to video tutorials? In particular I need to overlay the image so I need to draw in the same coordinate space. While you're all busy downvoting this question I'll be researching "canvas" and "drawing over images".
You can "highlight" mapped portions of your img like this:
Exactly overlay a canvas element of the same size using CSS
Tell canvas not to respond to mouse/touch events: pointer-events:none
When a mapped area is clicked, tell canvas to draw that area in a low-opacity fill using path commands:
context.beginPath();
context.moveTo(381,90);
context.lineTo(386,64);
context.lineTo(421,32);
context.lineTo(462,19);
context.lineTo(501,32);
context.lineTo(535,62);
context.lineTo(540,83);
context.closePath();
// fill the path area with a low-opacity red (or color of you choosing)
context.globalAlpha=0.15;
context.fillStyle='red';
context.fill(); // this fills the path
context.globalAlpha=1.00; // just resetting to default opacity
That allows you to keep your existing code that uses img.
Alternatively, if your design permits significant refactoring, you can draw the image on canvas and use context.isPointInPath to hit-test each path versus the mouse-click position. Then fill the hit path with the low-opacity fill.
[ Addition: Saving area coordinates for later use in hit-testing ]
Warning: untested code, might need tweaking
For easy re-use of each areas coordinates, you can put each set of coordinates in an object and put those objects in an array:
var areas=[];
// first area coordinates
arrays.push([ {x:381,y:90}, {x:386,y:64}, ... etc ]);
// second area coordinates
arrays.push([ {x:509,y:115}, {x:511,y:127}, ... etc ]);
...
Then use those saved area coordinates to do your hit-testing:
function isMouseInArea(mouseX,mouseY){
var index; // index of any "hit" area. leave it initially null
for(var i=0;i<areas.length;i++){
// create a path from this area's coordinates
defineAreaPath(areas[i]);
// test if the mouse is inside this area
if(ctx.isPointInPath(mouseX,mouseY)){
index=i;
}
}
// return any "hit" index of areas[] or return null if no hits
return(index);
}
// utility function to define a path from the supplied coordinates
function defineAreaPath(pts){
// create a path from this area's coordinates
ctx.beginPath();
ctx.moveTo(pts[0].x,pts[0].y);
for(var i=1;i<pts.length;i++){
ctx.lineTo(pts[i].x,pts[i].y);
}
ctx.closePath();
}
And you can use the isMouseInArea function like this:
var index=isMouseInArea(mouseX,mouseY)
if( index ){
// draw the area path that was "hit" by the mouse
defineAreaPath(areas[index]);
// fill that path with low-opacity fill
ctx.globalAlpha=0.15;
ctx.fillStyle='red';
ctx.fill();
ctx.globalAlpha=1.00;
}
From responses to other related questions it appears the HTML committee has been squabbling about image maps for canvas for five years.
Short version: you can't.
Long version: Use a canvas, set the image as its background and do your own hit testing.
Related
I have been making a basic painting application similar to MS-Paint with basic paint, eraser and fill tools. It's this last one that's giving me some trouble.
I'm pretty new to using BitmapData but the idea is that when the user clicks the board, it triggers the startFloodFill method. This is shown below:
public static function startFloodFill(e:MouseEvent):void
{
trace("FLOODFILL");
var boardRef:MovieClip = e.currentTarget.parent.board; //Creates a reference to the board
var boardData:BitmapData = new BitmapData(boardRef.width, boardRef.height); //Creates a new BitmapData with the same size as boardRef
boardData.floodFill(e.localX, e.localY, 0x00CCCCCC); //Applies the FloodFill
boardData.draw(boardRef); //Saves the boardRef as bitmapData
boardRef.bitmapData = boardData; //Updates the board
boardRef.parent.addChild(boardRef);
}
Can anybody tell me what I've done wrong here? When I click, the board does not change. I expected the FloodFill to fill the entire bitmap with the chosen colour as the board is blank when I click.
I also tried replacing the last two lines with:
boardRef.addChild(new Bitmap(boardData) ); //Updates the board
Thanks
The problem is that you first use the floodFill, and then use the draw method, which actually fills the BitmapData with whatever param you give it - in your case it's the boardRef.
You should first draw the boardRef into the boardData, and then use floodFill. At the end, you need to create new Bitmap and display it.
Now you are setting the bitmapData of a MovieClip?! Don't know what you wanted, but you just need to add new child (new Bitmap)
I want to make so that when I run a function it changes the image of objects in a function and I thought this would work
for(var i = 0; i < heads.length; i++){heads[i].src = ram_head;}
with ram_head equaling a string with the url but when I run the code the image does not change at all when I run the function.
Example of the code: http://jsfiddle.net/themagicalcake/URvA7
Here's code that:
creates an array of image objects (heads)
assigns the same image URL to all of those image objects (rams_head)
and when each image has fully loaded draws that image to the canvas.
Example code and a Demo: http://jsfiddle.net/m1erickson/5DXe7/
var ram_head="https://dl.dropboxusercontent.com/u/139992952/multple/RAMDISEGNO.jpg";
var heads=[];
heads.push(new Image());
heads.push(new Image());
for(var i=0;i<heads.length;i++){
heads[i].onload=function(){
ctx.drawImage(this,this.x,10);
}
heads[i].x=i*100;
heads[i].src=ram_head;
}
I see three issues with the code you've now linked to:
When you set the .src of an Image object, you can't use that image to draw with until the new .src URL image has loaded. If you want to change the .src and then draw, you have to handle the onload event and draw when that fires.
In your onkeydown handler, you are setting heads[i].src, but the image object is heads[i].img so the .src of the image would be heads[i].img.src.
Since you aren't displaying native <img> tags in the DOM, but are instead drawing the image yourself, after you change the .src of the image and then wait for that new image to load, if you want the image to display in your drawing context, you have to redraw it yourself. I won't just redraw just because you changed the .src.
I am trying to display a google static map, which when clicked, will open up a larger iframe, where the user can pan, zoom, etc.
JSFiddle here
Code below:
<div>
<a class="various fancybox.iframe" title="Whitehouse - USA" href="https://maps.google.com/maps?f=d&source=s_d&saddr=&daddr=1600+Pennsylvania+Ave+NW,+White+House,+Washington,+DC+20500&hl=en&geocode=Ca3jx5Eq6BcjFQ6IUQIdG4Ro-ynPaZnjvLe3iTGGOSyaFzTP2g&sll=38.897678,-77.036517&sspn=0.009644,0.01443&g=1600+Pennsylvania+Avenue+Northwest,+Washington,+DC&mra=ls&ie=UTF8&t=m&ll=38.89768,-77.036519&spn=0.008016,0.013733&z=16&output=embed">
<img src="http://maps.googleapis.com/maps/api/staticmap?center=1600+Pennsylvania+Ave+NW,+White+House,+Washington,+DC+20500&markers=1600+Pennsylvania+Ave+NW,+White+House,+Washington,+DC+20500&size=300x300&sensor=false">
</a>
</div>
I have tried to look for the non-javascript documentation relating to the iframe,but haven't come across anything. I would like to add the following to the iframe:
Center on the marker - The JSFiddle appears centered, but the exact same code run on the production site renders an iframe with the marker appearing in the top left.
Remove the marker label "B"
Input my own coordinates from my database - for example... do the same for New York City, Chicago, etc.. However, I have tried changing the daddr (destination address), but am unsure what the other variable stand for (i.e. sll, sspn, g, mra, ll, etc.)
Get directions - insert starting point, and get directions to pre-determined destination
At first a explanation of the parameters you need:
f
has to be d for directions
saddr
the start-address, may be a string(would be geolocated) or a latLng
daddr
the destination-address, may be a string(would be geolocated) or a latLng
ll
where to center the map(latlng) .when ommited, the map will be centered based on the markers
z
the zoom of the map. When ommitted the map will be zoomed based on the direction
output
has to be embed for iframe
A detailed list and explanation of the parameters you'll find at http://www.seomoz.org/ugc/everything-you-never-wanted-to-know-about-google-maps-parameters
However: you should note that none of the parameters is a part of any official API, it may change every day
The issues:
Center on the marker:
the marker could not be centered, because the iframe isn't visible when the map starts loading, unable to determine the size of the iframe . You could use a workaround:
First load a dummy-page into the iframe, when the fancybox is open, load the map.
This can be done by adding this to the fancybox-options:
beforeLoad: function(){
//store the original href for later use
this.oldhref=this.href;
//replace the href with some dummy-page
this.href='wait.htm';
return true;
},
afterLoad:function(){
//load the originally requested page to the iframe
$('.fancybox-iframe').attr('src',this.oldhref);
}
Remove the marker label "B"
there is no option to remove the B, all you can to is replace it with an A .
Therefore you must set the marker as the marker for the start-destination (saddr)
Input my own coordinates from my database
apply the coordinates to saddr or daddr(depending on what it should be, start or destination)
Get directions - insert starting point, and get directions to pre-determined destination
see 3.
Finally: you should consider to create a own map using the Maps-Javascript-API to get a map that you can handle yourself.
Is it possible to change a frame label within a gotoAndStop('label') with the parameters in a function?
I'm playing around with updating code as I learn more and more techniques, and at the moment the code is a basic click-a-button to select the object shape, and on press the button disappears:
// Change the object into a circle.
circle_btn.addEventListener(MouseEvent.CLICK,function(){changeShape_fun(circle_btn,circle);});
// Change the object into a square.
square_btn.addEventListener(MouseEvent.CLICK,function(){changeShape_fun(square_btn,square);});
// Change the object into a star.
star_btn.addEventListener(MouseEvent.CLICK,function(){changeShape_fun(star_btn,star);});
function changeShape_fun(shape_btn,frame){
shape_btn.visible = false;
main_mc.gotoAndStop('frame');
}
However I can't/don't seem to know how to change a frame label through function parameters, or if what I'm trying to do is even possible.
Also to note, while I'm all ears for any more efficient ways of doing what I'm trying to do, I would still like to know how/if you can change frame labels through function parmeters.
Thanks! :)
You're very close, but you're trying to go to a frame called 'frame' and not the string contained within the frame variable. Try this instead:
// Change the object into a circle.
circle_btn.addEventListener(MouseEvent.CLICK,function(event:MouseEvent){changeShape_fun(circle_btn, 'circle');});
// Change the object into a square.
square_btn.addEventListener(MouseEvent.CLICK,function(event:MouseEvent){changeShape_fun(square_btn, 'square');});
// Change the object into a star.
star_btn.addEventListener(MouseEvent.CLICK,function(event:MouseEvent){changeShape_fun(star_btn, 'star');});
function changeShape_fun(shape_btn,frame){
shape_btn.visible = false;
main_mc.gotoAndStop(frame);
}
This will go to a frame within main_mc called 'circle' when you click the circle or 'square' if you click the square, etc.
I have lots of identical images which share a common map:
<map name="mymap">
<area shape="polygon" coords="0,0,64,0,32,32" href="ref1">
<area shape="polygon" coords="64,0,64,64,32,32" href="ref2">
</map>
<img src="image.jpg" usemap="#mymap">
<img src="image.jpg" usemap="#mymap">
and I would like the followed link to depend on:
1. which image was clicked,
2. where in the image the click was.
Above, I differentiate the locations in the image, but I wonder if there is a way to do something different depending on which image was clicked?
You could write some JavaScript to clone the map, rename it and assign it to a list of images.
or
Capture the mouse click coordinates and determine where the click is is relation to which image. If they're in a row you only need to check one coordinate.
You can trigger JavaScript using the href:
<area shape="polygon" coords="0,0,64,0,32,32" href="javascript:myFunction(1)">
<area shape="polygon" coords="0,0,64,0,32,32" href="javascript:myFunction(2)">
if you give each image an id attribute, i think you might be able to use a little bit of jquery to target the map element of each image and change the location of the window in javascript.
something along the lines of
$("#imgId map").click(function(){
window.location = "http://www.google.com";
}
$("#imgId2 map").click(function(){
window.location = "http://www.stackoverflow.com";
}
depending on the amount of images you have, if this seems like a lot or too repetitive, there might be a smarter way of being able to handle the links in javascript automatically.