动态调整图像地图和图片大小

42

我目前正在尝试在我的网站上制作一个图像地图,它会根据窗口的大小进行调整...我想知道是否有用HTML实现的方法,还是必须使用Javascript或其他语言来完成。

<div style="text-align:center; width:1920px; margin-left:auto; margin-right:auto;">
<img id="Image-Maps_5201211070133251" src="Site.png" usemap="#Image-Maps_5201211070133251" border="0" width="1920" height="1080" alt="" />
<map id="_Image-Maps_5201211070133251" name="Image-Maps_5201211070133251">
<area shape="poly" coords="737,116,1149,118,944,473," href="http://essper.bandcamp.com" alt="Bandcamp" title="Bandcamp"   />
<area shape="poly" coords="1006,589,1418,590,1211,945," href="http://soundcloud.com/essper" alt="Soundcloud" title="Soundcloud"   />
<area shape="poly" coords="502,590,910,591,708,944," href="http://facebook.com/the.essper" alt="Facebook" title="Facebook"   />
</map>


你可以使用CSS来调整div和/或img的大小,但由于区域的坐标是绝对的,因此您可能需要使用JavaScript来完成这个任务。 - Horen
如果我误解了您的问题,请纠正我。您有一个固定大小并居中的 IMG,它不会根据窗口调整大小,现在您想要一个 MAP,它始终只覆盖 IMG 的可见部分? - Teemu
不,我正在为图像和地图添加动态功能,然后根据图像的大小进行调整。 - ultrazoid
13个回答

62

12
太棒了!我希望我可以多次点赞。 - Grodriguez
3
这太棒了,像魔法般地起作用,节省了我很多时间!@craphunter:在我看来,应该为代码高亮编写一个不同的插件,David的这个库非常适合它的用途! - Oliver M Grech
嗨,大卫。这适用于响应式图片吗?即最初宽度为100%的图像?谢谢。 - Steve
@Steve,是的,那就是它的主要原因。 - David Bradshaw
1
这是一个非常棒的库,谢谢。我建议对代码进行一些小改动,以处理哈希变化。随着单页应用/React/Vue等的兴起,这尤其有用。 - zfrisch
显示剩余4条评论

52
如果您需要使用JavaScript完成任务,这是一个跨浏览器的代码片段,用于调整MAP元素中的所有区域大小。
window.onload = function () {
    var ImageMap = function (map) {
            var n,
                areas = map.getElementsByTagName('area'),
                len = areas.length,
                coords = [],
                previousWidth = 1920;
            for (n = 0; n < len; n++) {
                coords[n] = areas[n].coords.split(',');
            }
            this.resize = function () {
                var n, m, clen,
                    x = document.body.clientWidth / 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 = document.body.clientWidth;
                return true;
            };
            window.onresize = this.resize;
        },
        imageMap = new ImageMap(document.getElementById('map_ID'));
    imageMap.resize();
}

previousWidth 必须等于原始图像的宽度。在 HTML 中,您还需要使用一些相对单位:

<div style="width:100%;">
<img id="Image-Maps_5201211070133251" src="Site.png" usemap="#Image-Maps_5201211070133251" border="0" width="100%" alt="" />

jsFiddle上有一个工作演示。如果在IE中打开这个演示,当单击AREA时,您实际上可以看到它们。


1
太好了,一个不使用jQuery的解决方案!谢谢! - Rolf
1
好的,我在这里发布了一个问题:https://dev59.com/Zn_aa4cB1Zd3GeqP8dfv - Barry Michael Doyle
1
@Steve 只需使用答案中的代码。"previousWidth 必须等于原始图像的宽度。" 答案中可能措辞有点不好,previousWidth 是在您最初创建 area 元素的坐标时图像具有的宽度。 - Teemu
1
@Steve 你有注意到Barry的问题和我对它的回答吗?那个回答中引入的一个小变化使ImageMapper更加通用。 - Teemu
这个答案有三个问题:1)它会覆盖之前设置的任何窗口调整大小事件,这也意味着它只能用于一个图像,2)如果有多个调整大小事件,它会随着时间的推移累积错误,3)它假设图像从固定宽度开始,这很可能比正确的情况更常见。 - moron4hire
显示剩余8条评论

5
这是我最简单的解决方案。 不需要jquery或任何插件。 请注意,这个解决方案不能处理标记中的任何错误,或大小不成比例的图像。

function mapResizer(maps) {
    if (!maps) {maps = document.getElementsByTagName('map');}
    for (const map of maps) {
        map.img = document.querySelectorAll(`[usemap="#${map.name}"]`)[0];
        map.areas = map.getElementsByTagName('area');
        for (const area of map.areas) {
            area.coordArr = area.coords.split(',');
        }
    }
    function resizeMaps() {
        for (const map of maps) {
            const scale = map.img.offsetWidth / (map.img.naturalWidth || map.img.width);
            for (const area of map.areas) {
                area.coords = area.coordArr.map(coord => Math.round(coord * scale)).join(',');
            }
        }
    }
    window.addEventListener('resize', () => resizeMaps());
    resizeMaps();
}
if (document.readyState == 'complete') {
    mapResizer();
} else {
    window.addEventListener('load', () => mapResizer());
}


请查看我对VueJS的改进。 - Cédric NICOLAS
这绝对是最短的解决方案。但它非常慢,需要很多时间。 - SilverSurfer

3
作为一个类(ES6):
class ResponsiveImageMap {
    constructor(map, img, width) {
        this.img = img;
        this.originalWidth = width;
        this.areas = [];

        for (const area of map.getElementsByTagName('area')) {
            this.areas.push({
                element: area,
                originalCoords: area.coords.split(',')
            });
        }

        window.addEventListener('resize', e => this.resize(e));
        this.resize();
    }

    resize() {
        const ratio = this.img.offsetWidth / this.originalWidth;

        for (const area of this.areas) {
            const newCoords = [];
            for (const originalCoord of area.originalCoords) {
                newCoords.push(Math.round(originalCoord * ratio));
            }
            area.element.coords = newCoords.join(',');
        }

        return true;
    };
}

使用方法:

var map = document.getElementById('myMapId');
var image = document.getElementById('myImageId');
new ResponsiveImageMap(map, image, 800);

3
如果您可以访问Illustrator或其他可以生成SVG的程序,使用SVG轻松创建动态图像映射是非常简单的。 这不需要任何编程。 以下是Illustrator的说明(仅需几秒钟):
  1. 在Illustrator中打开图像,并保存为新名称
  2. 将文档大小调整为与图像相同
  3. 为地图部分绘制填充矩形(将透明度设置为50%会很有帮助)
  4. 使用“属性”面板为每个矩形添加链接
  5. 将所有矩形的不透明度更改为0%
  6. 选择图像,并在“链接”面板菜单中选择“取消嵌入…”(名称无关紧要,我们不会使用该图像)
  7. 文件 › 另存为SVG(图像位置:链接,CSS属性:样式元素,勾选响应式)
  8. 打开生成的svg文件
  9. 删除前两行(XML和Adobe注释)
  10. 更新图像源
  11. 将svg代码粘贴到您的HTML文档中
这适用于所有主流浏览器。以下是Illustrator的SVG导出设置截图: enter image description here

3

您可以将坐标乘以原始图像和样式化图像的比率。

<img id="paredea" usemap="#PAREDE-A"  src="https://i.imgur.com/o9nrUMR.png">

    <map name="PAREDE-A">
        <area id="paredea0" shape="rect"  onclick="alert('colmeia A')">
        <area id="paredea1" shape="rect"  onclick="alert('colmeia B')">
        <area id="paredea2" shape="rect"  onclick="alert('colmeia C')">
        <area id="paredea3" shape="rect"  onclick="alert('colmeia D')">
        <area id="paredea4" shape="rect"  onclick="alert('colmeia E')"> 

        <area id="paredea5" shape="rect"  onclick="alert('comeia F')">
        <area id="paredea6" shape="rect"  onclick="alert('colmeia G')">
        <area id="paredea7" shape="rect"  onclick="alert('colmeia H')">
        <area id="paredea8" shape="rect"  onclick="alert('colmeia I')">
        <area id="paredea9" shape="rect"  onclick="alert('colmeia J')">  

        <area id="paredea10" shape="rect"  onclick="alert('colmeia K')">
        <area id="paredea11" shape="rect"  onclick="alert('colmeia L')">
        <area id="paredea12" shape="rect"  onclick="alert('colmeia M')">
        <area id="paredea13" shape="rect"  onclick="alert('colmeia N')">
        <area id="paredea14" shape="rect"  onclick="alert('colmeia O')">  
    </map>

    <script>


        var coordsA = [];
        coordsA[0] = "0,0,200,130";
        coordsA[1] = "200,0,400,130";
        coordsA[2] = "400,0,600,130";
        coordsA[3] = "600,0,800,130";
        coordsA[4] = "800,0,1000,130";

        coordsA[5] = "0,160,200,240";
        coordsA[6] = "200,160,400,240";
        coordsA[7] = "400,160,600,240";
        coordsA[8] = "600,160,800,240";
        coordsA[9] = "800,160,1000,240";

        coordsA[10] = "0,270,200,400";
        coordsA[11] = "200,270,400,400";
        coordsA[12] = "400,270,600,400";
        coordsA[13] = "600,270,800,400";
        coordsA[14] = "800,270,1000,400";


        function setcoords(areaid, totalOfAreas) {
            document.getElementById('paredea').style.width = "auto";
            var width1 = document.getElementById('paredea').width;
            document.getElementById('paredea').style.width = "100%";
            var width2 = document.getElementById('paredea').width;
            var ratio = width2 / width1;

            for (var i = 0; i < totalOfAreas; i++) {
                var temp = coordsA[i].split(",");
                var newcoords = "";
                for (var j = 0; j < temp.length; j++) {
                    temp[j] *= ratio;
                    newcoords += temp[j] + ",";
                }
                newcoords = newcoords.substr(0, newcoords.length - 1);

                document.getElementById(areaid + i).coords = newcoords;
            }
        }


       window.onload = function () {
            setcoords("paredea", 15);
        };

        window.onresize = function () {
            setcoords("paredea", 15);
        };
    </script>

1
为了使其起作用,您需要拥有带有原始图片的coordsdata-original-coords属性。
$(function () {
    function adjeustCoords() {
        var image=$('img'); //change that to your image selector
        var originalWidth=image[0].naturalWidth;
        var currentWidth=image.width();
        var ratio=currentWidth/originalWidth;
        $("map area").each(function(){
            //change that to your area selector
            var coords=$(this).attr('data-original-coords').split(',');
            coords = coords.map(function (x) {
                return Math.round(x*ratio);
                //i don't know if all browsers can accept floating point so i round the result
            });
            $(this).attr('coords',coords.join());
        });
    }
    adjeustCoords();
    $(window).resize(function(){
        adjeustCoords();
    });
});

这适用于 Chrome、Firefox 和 Edge 的最新版本。

1

文档含糊不清:它只适用于 .svg 图像还是所有类型的图像(jpg、png...)? - Moebius
肯定的。看起来它只能处理.svg文件。如果是这种情况,你应该清楚地说明。如果不是,你应该跳过所有关于svg的引用。两件事都没有清楚地说明。 - Moebius
当然,但我认为我的建议仍然适用。如果我花了这么长时间来弄清楚它,那可能是因为我没有专注,但也可能是因为它很混乱。无论如何,还是谢谢。 - Moebius

0

我只对矩形坐标进行了测试,但我认为它应该推广到圆形或多边形。

function wrap ( img, map ) {
  var originalCoords = [ ],
      test = new Image();

  for ( var i = 0; i < map.areas.length; ++i ) {
    var coords = map.areas[i].coords;
    originalCoords.push( coords.split( "," ).map( parseFloat ) );
  }

  function resize () {
    var ratio = img.width / test.width;
    for ( var i = 0; i < map.areas.length; ++i ) {
      map.areas[i].coords = originalCoords[i].map( function ( n ) {
        return ratio * n;
      } ).join( "," );
    }
  }

  test.addEventListener( "load", function () {
    window.addEventListener( "resize", resize, false );
    resize();
  }, false );

  test.src = img.src;
}

var imgs = document.querySelectorAll( "img[usemap]" );
for ( var i = 0; i < imgs.length; ++i ) {
  var map = document.querySelector( "map[name=" + imgs[i].useMap.substring( 1 ) + "]" );
  wrap( imgs[i], map );
}

0
你可以使用CSS精灵来实现这个功能。将所有的图片拼接成一张图片,这样你只需要发起一个HTTP请求就能加载所有的图片。这种技术不需要JavaScript,你只需要使用background-position;属性来移动你的图片。
这是一种有效的页面优化技术。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接