I have an existing image map in a responsive html layout. Images scale according to browser size, but the image coordinates are obviously fixed pixel sizes. What options do I have to resize the image map coordinates?
617 Answers
For responsive image maps you will need to use a plugin:
Or
No major browsers understand percentage coordinates correctly, and all interpret percentage coordinates as pixel coordinates.
And also this page for testing whether browsers implement
6You can also use SVG instead of an image map. There is a tutorial on how to do this. Sample below, imported from this jsfiddle.
.hover_group:hover { opacity: 1; } #projectsvg { position: relative; width: 100%; padding-bottom: 77%; vertical-align: middle; margin: 0; overflow: hidden; background: lightgreen; } #projectsvg svg { display: inline-block; position: absolute; top: 0; left: 0; }<figure> <svg version="1.1" xmlns="" xmlns:xlink="" viewBox="0 0 1920 1080" preserveAspectRatio="xMinYMin meet"> <!-- set your background image --> <image width="1920" height="1080" xlink:href="" /> <!-- create the regions --> <g opacity="0"> <a xlink:href=""> <text x="652" y="706.9" font-size="20">First zone</text> <rect x="572" y="324.1" opacity="0.2" fill="#FFFFFF" width="264.6" height="387.8"></rect> </a> </g> <g opacity="0"> <a xlink:href=""> <text x="1230.7" y="952" font-size="20">Second zone</text> <rect x="1081.7" y="507" opacity="0.2" fill="#FFFFFF" width="390.2" height="450"></rect> </a> </g> </svg> </figure>6Responsive Image Maps jQuery Plugin by Matt Stow
5I found a no-JS way to address this. The hit areas will be rectangular unless you change the border-radius to make circular or oval areas.
First of all, make sure your image is in a div that's relatively positioned. Then put the image inside this div, which means it'll take up all the space in the div. Finally, add absolutely positioned div's under the image, within the main div, and use percentages for top, left, width, and height to get the link hit areas the size and position you want.
I find it's easiest to give the div a black background color (ideally with some alpha fading so you can see the linked content underneath) when you're first working, and to use a code inspector in your browser to adjust the percentages in real time, so that you can get it just right.
Here's the basic outline you can work with. By doing everything with percentages, you ensure the elements all stay the same relative size and position as the image scales.
<div> <img src="background-image.png"> <a href="/link1"><div></div></a> <a href="/link2"><div></div></a> </div> Use this code with your code inspector in Chrome or your browser of choice, and adjust the percentages (you can use decimal percentages to be more exact) until the boxes are just right. Also choose a background-color of transparent when you're ready to use it since you want your hit areas to be invisible.
I ran across a solution that doesn't use image maps at all but rather anchor tags that are absolutely positioned over the image. The only drawback would be that the hotspot would have to be rectangular, but the plus is that this solution doesn't rely on Javascript, just CSS. There is a website that you can use to generate the HTML code for the anchors:
I put the image and the generated anchor tags in a relatively positioned div tag and everything worked perfectly on window resize and on my mobile phone.
5David Bradshaw wrote a nice little library that solves this problem. It can be used with or without jQuery.
2The following method works perfectly for me, so here's my full implementation:
<img src="my.png" width="924" height="330" border="0" usemap="#map" /> <map name="map"> <area shape="poly" coords="774,49,810,21,922,130,920,222,894,212,885,156,874,146" href="#mylink" /> <area shape="poly" coords="649,20,791,157,805,160,809,217,851,214,847,135,709,1,666,3" href="#myotherlink" /> </map> <script> $(function(){ var image_is_loaded = false; $("#my_image").on('load',function() { $(this).data('width', $(this).attr('width')).data('height', $(this).attr('height')); $($(this).attr('usemap')+" area").each(function(){ $(this).data('coords', $(this).attr('coords')); }); $(this).css('width', '100%').css('height','auto').show(); image_is_loaded = true; $(window).trigger('resize'); }); function ratioCoords (coords, ratio) { coord_arr = coords.split(","); for(i=0; i < coord_arr.length; i++) { coord_arr[i] = Math.round(ratio * coord_arr[i]); } return coord_arr.join(','); } $(window).on('resize', function(){ if (image_is_loaded) { var img = $("#my_image"); var ratio = img.width()/img.data('width'); $(img.attr('usemap')+" area").each(function(){ console.log('1: '+$(this).attr('coords')); $(this).attr('coords', ratioCoords($(this).data('coords'), ratio)); }); } }); }); </script> Working for me (remember to change 3 things in code):
previousWidth (original size of image)
map_ID (id of your image map)
img_ID (id of your image)
HTML:
<div> <img src="" usemap="#map" border="0" width="100%" alt="" /> </div> <map name="map"> <area shape="poly" coords="48,10,80,10,65,42" href="javascript:;" alt="Bandcamp" title="Bandcamp" /> <area shape="poly" coords="30,50,62,50,46,82" href="javascript:;" alt="Facebook" title="Facebook" /> <area shape="poly" coords="66,50,98,50,82,82" href="javascript:;" alt="Soundcloud" title="Soundcloud" /> </map> Javascript:
window.onload = function () { var ImageMap = function (map, img) { var n, areas = map.getElementsByTagName('area'), len = areas.length, coords = [], previousWidth = 128; for (n = 0; n < len; n++) { coords[n] = areas[n].coords.split(','); } this.resize = function () { var n, m, clen, x = img.offsetWidth / previousWidth; for (n = 0; n < len; n++) { clen = coords[n].length; for (m = 0; m < clen; m++) { coords[n][m] *= x; } areas[n].coords = coords[n].join(','); } previousWidth = img.offsetWidth; return true; }; window.onresize = this.resize; }, imageMap = new ImageMap(document.getElementById('map_ID'), document.getElementById('img_ID')); imageMap.resize(); return; } consider to use this image mapper
its SVG based and its responsive and
it have good wizards to build the map
its simple enough for the page because it use no library
2Check out the image-map plugin on Github. It works both with vanilla JavaScript and as a jQuery plugin.
$('img[usemap]').imageMap(); // jQuery ImageMap('img[usemap]') // JavaScript Check out the demo.
0I come across with same requirement where, I wants to show responsive image map which can resize with any screen size and important thing is, i want to highlight that coordinates.
So i tried many libraries which can resize coordinates according to screen size and event. And i got best solution(jquery.imagemapster.min.js) which works fine with almost all browsers. Also i have integrated it with Summer Plgin which create image map.
var resizeTime = 100; var resizeDelay = 100; $('img').mapster({ areas: [ { key: 'tbl', fillColor: 'ff0000', staticState: true, stroke: true } ], mapKey: 'state' }); // Resize the map to fit within the boundaries provided function resize(maxWidth, maxHeight) { var image = $('img'), imgWidth = image.width(), imgHeight = image.height(), newWidth = 0, newHeight = 0; if (imgWidth / maxWidth > imgHeight / maxHeight) { newWidth = maxWidth; } else { newHeight = maxHeight; } image.mapster('resize', newWidth, newHeight, resizeTime); } function onWindowResize() { var curWidth = $(window).width(), curHeight = $(window).height(), checking = false; if (checking) { return; } checking = true; window.setTimeout(function () { var newWidth = $(window).width(), newHeight = $(window).height(); if (newWidth === curWidth && newHeight === curHeight) { resize(newWidth, newHeight); } checking = false; }, resizeDelay); } $(window).bind('resize', onWindowResize);img[usemap] { border: none; height: auto; max-width: 100%; width: auto; }<script src=""></script> <script src=""></script> <img src="" alt="" usemap="#map" /> <map name="map"> <area shape="poly" coords="777, 219, 707, 309, 750, 395, 847, 431, 916, 378, 923, 295, 870, 220" href="#" alt="poly" title="Polygon" data-maphilight='' state="tbl"/> <area shape="circle" coords="548, 317, 72" href="#" alt="circle" title="Circle" data-maphilight='' state="tbl"/> <area shape="rect" coords="182, 283, 398, 385" href="#" alt="rect" title="Rectangle" data-maphilight='' state="tbl"/> </map>Hope help it to someone.
It depends, you can use jQuery to adjust the ranges proportionally I think. Why do you use an image map by the way? Can't you use scaling divs or other elements for it?
1For those who don't want to resort to JavaScript, here's an image slicing example:
As you scale the window, the clown image will scale accordingly, and when it does, the nose of the clown remains hyperlinked.
2I have created a javascript version of the solution Tom Bisciglia suggested.
My code allows you to use a normal image map. All you have to do is load a few lines of CSS and a few lines of JS and... BOOM... your image map has hover states and is fully responsive! Magic right?
var images = document.querySelectorAll('img[usemap]'); images.forEach( function(image) { var mapid = image.getAttribute('usemap').substr(1); var imagewidth = image.getAttribute('width'); var imageheight = image.getAttribute('height'); var imagemap = document.querySelector('map[name="'+mapid+'"]'); var areas = imagemap.querySelectorAll('area'); image.removeAttribute('usemap'); imagemap.remove(); // create wrapper container var wrapper = document.createElement('div'); wrapper.classList.add('imagemap'); image.parentNode.insertBefore(wrapper, image); wrapper.appendChild(image); areas.forEach( function(area) { var coords = area.getAttribute('coords').split(','); var xcoords = [parseInt(coords[0]),parseInt(coords[2])]; var ycoords = [parseInt(coords[1]),parseInt(coords[3])]; xcoords = xcoords.sort(function(a, b){return a-b}); ycoords = ycoords.sort(function(a, b){return a-b}); wrapper.innerHTML += "<a href='"+area.getAttribute('href')+"' title='"+area.getAttribute('title')+"' class='area' style='left: "+((xcoords[0]/imagewidth)*100).toFixed(2)+"%; top: "+((ycoords[0]/imageheight)*100).toFixed(2)+"%; width: "+(((xcoords[1] - xcoords[0])/imagewidth)*100).toFixed(2)+"%; height: "+(((ycoords[1] - ycoords[0])/imageheight)*100).toFixed(2)+"%;'></a>"; }); });img {max-width: 100%; height: auto;} .imagemap {position: relative;} .imagemap img {display: block;} .imagemap .area {display: block; position: absolute; transition: box-shadow 0.15s ease-in-out;} .imagemap .area:hover {box-shadow: 0px 0px 1vw rgba(0,0,0,0.5);}<!-- Image Map Generated by --> <img src="" width="2000" height="2604" usemap="#image-map"> <map name="image-map"> <area target="" alt="Zirconia Abutment" title="Zirconia Abutment" href="/" coords="3,12,199,371" shape="rect"> <area target="" alt="Gold Abutment" title="Gold Abutment" href="/" coords="245,12,522,371" shape="rect"> <area target="" alt="CCM Abutment" title="CCM Abutment" href="/" coords="564,12,854,369" shape="rect"> <area target="" alt="EZ Post Abutment" title="EZ Post Abutment" href="/" coords="1036,12,1360,369" shape="rect"> <area target="" alt="Milling Abutment" title="Milling Abutment" href="/" coords="1390,12,1688,369" shape="rect"> <area target="" alt="Angled Abutment" title="Angled Abutment" href="/" coords="1690,12,1996,371" shape="rect"> <area target="" alt="Temporary Abutment [Metal]" title="Temporary Abutment [Metal]" href="/" coords="45,461,506,816" shape="rect"> <area target="" alt="Fuse Abutment" title="Fuse Abutment" href="/" coords="1356,461,1821,816" shape="rect"> <area target="" alt="Lab Analog" title="Lab Analog" href="/" coords="718,935,1119,1256" shape="rect"> <area target="" alt="Transfer Impression Coping Driver" title="Transfer Impression Coping Driver" href="/" coords="8,1330,284,1731" shape="rect"> <area target="" alt="Impression Coping [Transfer]" title="Impression Coping [Transfer]" href="/" coords="310,1330,697,1731" shape="rect"> <area target="" alt="Impression Coping [Pick-Up]" title="Impression Coping [Pick-Up]" href="/" coords="1116,1330,1560,1733" shape="rect"> </map>Similar to Orland's answer here:
Combined with Chris' code here:
If the areas fit in a grid you can overlay the areas by transparent pictures using a width in % that keep their aspect ratio.
.wrapperspace { width: 100%; display: inline-block; position: relative; } .mainspace { position: absolute; top: 0; bottom: 0; right: 0; left: 0; }<div> <img title="" src="background-image.png" width="100%" /> <div> <div> <img src="space-top.png"/> </div> <div> <a href=""><img src="space-company.png"></a> </div> <div> <a href=""><img src="space-company.png"></a> <a href=""><img src="space-company.png"></a> </div> </div> </div>You can use a margin in %. Additionally "space" images can be placed next to each other inside a 3rd level div.
For some reason none of these solutions worked for me. I've had the best success using transforms.
transform: translateX(-5.8%) translateY(-5%) scale(0.884); responsive width && height
window.onload = function() { var ImageMap = function(map, img) { var n, areas = map.getElementsByTagName('area'), len = areas.length, coords = [], imgWidth = img.naturalWidth, imgHeight = img.naturalHeight; for (n = 0; n < len; n++) { coords[n] = areas[n].coords.split(','); } this.resize = function() { var n, m, clen, x = img.offsetWidth / imgWidth, y = img.offsetHeight / imgHeight; imgWidth = img.offsetWidth; imgHeight = img.offsetHeight; for (n = 0; n < len; n++) { clen = coords[n].length; for (m = 0; m < clen; m += 2) { coords[n][m] *= x; coords[n][m + 1] *= y; } areas[n].coords = coords[n].join(','); } return true; }; window.onresize = this.resize; }, imageMap = new ImageMap(document.getElementById('map_region'), document.getElementById('prepay_region')); imageMap.resize(); return; } 1