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:
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;
}