如何在<img/>上放置非矩形可点击“图钉”元素?

时间:2016-07-02 20:21:17

标签: html image

基本上,我必须在HTML / CSS / Javascript中创建它: enter image description here

如你所见,它是纽约的地图。当用户点击地图下方的其中一个图标时,地图左下方的图钉应显示在某些位置。然后,用户应该可以单击它们,以便从其中弹出另一个带有一些文本的div。这是我不知所措的地方。这就是我到目前为止的想法:

- 创建另外3个图像,其中包含所有动力装置销,所有造船厂销和所有钢厂销钉,并使用z-index将它们定位在地图顶部。唯一的问题是,用户只能点击具有最高z-index的图像。例如,如果他们同时启用造船厂和钢厂,他们只能点击最后添加的销钉。这是我放弃这个想法的地方。

- 使用具有绝对位置的div来复制地图的细分。但是,div是矩形的,而细分则不是。所以这也不好。

- 使用图像映射。自1990年代以来,我没有听说有人使用这些。无论如何,我不知道地图会如何帮助我。我需要定位它们,而不是超链接它们。

我愿意接受任何建议/插件。

1 个答案:

答案 0 :(得分:4)

嗯,你可能不喜欢这个答案,但是因为没有其他人提出任何建议,这就是我的做法(或者实际上可能使用多层图像地图,但是你的引脚需要在图像上):

选项1:将整个地图和图钉作为SVG并将svg代码粘贴到您的html中,并将javascript点击事件附加到它。 SVG元素可以使用与html元素相同的DOM事件,可以定位,缩放和整形。您甚至可以使用CSS规则和类隐藏它们!

SVG是一个非常优雅的解决方案,拥有可靠的浏览器支持,如果能够以这种形式获得您的艺术资产,那么我可能就是这样做的。您可以在线获取svg的各种工具和教程,因此我将其留在此处,只需记住javascript事件就可以了!

选项2:使用HTML自己动手。让我更多地说明这一点,因为它在网络上并不是标准,尽管它与桌面应用程序中使用的理念相同。

  1. 创建四个图像:地图和图钉。

  2. 创建一个数据数组,该数组是该引脚的位图,显示透明且可单击的部分。这是一个带有1的矩形,您可以在其中单击0,在此处可以形成形状。这种技术在桌面程序中用于鼠标光标和类似的东西。最简单的方法是使用图像编辑器将其清除为白色和黑色,然后导出。 GIMP可以导出到xpm,您可以手动修复,以便轻松地制作成Javascript。

  3. 创建每个引脚位置的数据数组。 (理想情况下,我将此作为绝对定位的HTML链接元素的列表,因为这是一种优雅地降级的数据格式 - 如果不完美 - 没有Javascript,但您不想在它们上侦听事件。)< / p>

  4. 在包含所有图像的div 上放置一个事件侦听器,并使用单击的坐标查找数据数组中的引脚。如果需要,使用位图下降 - 循环遍历可能的对象并查看您是否在边界矩形中。如果是这样,请检查位图。如果它是可点击的,你有一个打击!如果没有,请转到下一个。

  5. 线性搜索最容易实现并且运行良好,但如果您有数千个项目可能需要稍微优化一下,您可以在网上搜索其他算法/数据结构,如四叉树(如果需要)。但是如果你有那么多物体,那么地图可能无法用户使用!

    所以基本上,点击将全部解决数据阵列的坐标而不是关闭HTML元素,图像仅供用户使用。

    我使用你的图像拍了一下这个技术的演示,只是通过切割+粘贴引脚来懒洋洋地劈砍它。如果你有原始图像,你可以做得更好。

    直播链接(可能是暂时的) http://arsdnet.net/demo.html

    为后代复制/粘贴代码:

    <!DOCTYPE html>
    <html>
    <head>
    <title>Demo of map idea</title>
    <script>
            window.onload = function() {
                    var map = document.querySelector(".ny-map");
                    // on each click on the map, we need to loop through
                    // the bounding boxes of the pins and see which one we hit.
                    map.addEventListener("click", function(event) {
                            var pins = map.querySelectorAll(".pin");
                            var i;
                            // we are searching BACKWARD so it checks
                            // for clicks on the topmost pin first, then
                            // proceeds down to the bottom ones.
                            for(i = pins.length - 1; i >= 0; i--) {
                                    var rect = pins[i].getBoundingClientRect();
                                    if (event.clientX >= rect.left
                                     && event.clientX < rect.right
                                     && event.clientY >= rect.top
                                     && event.clientY < rect.bottom)
                                    {
                                            // if we're inside the bounding box,
                                            // next we need to check the transparency
                                            // bitmap to see if it is an actual hit
    
                                            var x = event.clientX - rect.left;
                                            var y = event.clientY - rect.top;
    
                                            // pinBitmap is defined below
                                            // "." happens to be the char my Gimp export
                                            // gave to the clickable region, so we check for it
                                            if(pinBitmap[y].charAt(x) == ".") {
                                                    // we hit this one!
                                                    // toggle class "showing"
                                                    if(pins[i].className.indexOf(" showing") == -1)
                                                            pins[i].className += " showing";
                                                    else
                                                            pins[i].className = pins[i].className.replace(" showing", "");
                                                    break; // all done
                                            }
                                            // if we didn't hit on the bitmap, continue
                                            // searching the one below by proceeding with the loop
                                    }
                            }
                    });
            };
    </script>
    </head>
    <body>
    <div class="ny-map">
            <div class="shipyard pin" style="left: 140px; top: 450px;">
                    <p>This is information about shipyard #1.</p>
            </div>
            <div class="shipyard pin" style="left: 150px; top: 450px;">
                    <p>This is information about shipyard #2, which overlaps shipyard #1.</p>
            </div>
            <div class="shipyard pin" style="left: 460px; top: 350px;">
                    <p>This is information about a shipyard pin in the Syracuse area.</p>
            </div>
    </div>
    
    <script>
    var pinBitmap = [
    "+++++++++++.++.............++++++++++++++",
    "+++++++++++..................++++++++++++",
    "++++++++++.....................++++++++++",
    "+++++++..........................++++++++",
    "+++++................................++++",
    "++.......................................",
    "....+...............................+++++",
    ".+++.................................++++",
    "+++...................................+++",
    "+++...................................+++",
    "++.....................................++",
    "++.....................................++",
    "+.......................................+",
    "+.......................................+",
    ".........................................",
    ".........................................",
    ".........................................",
    ".........................................",
    ".........................................",
    ".........................................",
    ".........................................",
    ".........................................",
    ".........................................",
    ".........................................",
    ".........................................",
    ".........................................",
    "+.......................................+",
    "+.......................................+",
    "+.......................................+",
    "++.....................................++",
    "++.....................................++",
    "+++...................................+++",
    "+++...................................+++",
    "+++...................................+++",
    "++++.................................++++",
    "++++.................................++++",
    "+++++...............................+++++",
    "+++++...............................+++++",
    "++++++.............................++++++",
    "++++++.............................++++++",
    "+++++++...........................+++++++",
    "+++++++...........................+++++++",
    "++++++++.........................++++++++",
    "+++++++++.......................+++++++++",
    "+++++++++.......................+++++++++",
    "++++++++++.....................++++++++++",
    "++++++++++.....................++++++++++",
    "+++++++++++....................++++++++++",
    "+++++++++++....................++++++++++",
    "++++++++++++...................++++++++++",
    "+++++++++++++...............+..++++++++++",
    "+++++++++++++...............+..++++++++++",
    "++++++++++++++.............++..++++++++++",
    "+++++++++++++++...........+++..++++++++++",
    "+++++++++++++++...........+++..++++++++++",
    "++++++++++++++++.........++++..++++++++++",
    "++++++++++++++++.........++++..++++++++++",
    "+++++++++++++++++.......+++++..++++++++++",
    "++++++++++.................++..++++++++++",
    "++++++.........................++++++++++",
    "++++..............................+++++++",
    "+++................................++++++",
    "+++................................++++++",
    "++++..............................+++++++",
    "++++++..........................+++++++++",
    "++++++++++..................+..++++++++++"];
    </script>
    
    <style>
    
    .ny-map {
            width: 985px;
            height: 815px;
            background-image: url('demo/qDOGS-fs8.png');
            position: relative;
    }
    
    .pin {
            width: 41px;
            height: 66px;
            margin-left: -20px;
            margin-top: -66px;
            position: absolute;
    }
    
    .shipyard.pin {
            background-image: url('demo/pin.png');
            background-repeat: no-repeat;
    }
    
    .pin:not(.showing) > * {
            display: none;
    }
    
    .pin.showing {
            background-color: rgba(255, 255, 255, 0.8);
            padding-left: 48px;
            padding-right: 4px;
            width: auto;
    }
    
    </style>
    
    </body>
    </html>
    

    这是我的表现方式。首先,处理奇怪形状的pinBitmap部分点击:

    取下图钉并在GIMP中打开它。使用颜色选择工具通过单击选择所有透明部分。使用桶填充,选择&#34;填写整个选择&#34;使它全黑。点击选择 - &gt;反转选择图像的其余部分并将其全部填充为白色。

    现在将文件导出为XPM。在你的文本编辑器中打开它,然后将第7行(好的,第一个带有图像数据的长文件,无论它在你的图像数据中)发送到最后并将其粘贴到Javascript中。

    我在其周围放置数组括号以生成pinBitmap变量,并且由于Gimp生成了字符串,我只是在函数中使用了字符串。

    文件底部的CSS生成我们的地图和图钉图像。 showing类管理更多信息的显示。您当然也可以执行.ny-map .powerplant { display: none; }等操作来切换事物的显示。搜索要点击的项目时,请确保使用computedStyle或仅使用某些标记变量显示这些项目,如果不是,则继续显示!

    您会注意到这些引脚本身在HTML中是半语义的,只是使用内联样式来定位它们。在CSS中,我做了一些负边缘技巧,让它在我想要坐标的位置显示更多的针脚点,但是你可以调整这个 - 但是JS解释了所有这些css的调整,信不信由你!只需从gimp或其他任何地方获取相对于图像的坐标。

    我在HTML中放置了有关引脚的详细信息,因此它也与内容很好地分组。

    最后,魔术函数位于文件的顶部。它只是地图上一个普通的事件监听器,它通过引脚循环。 getBoundingClientRect函数是标准DOM的一部分,并生成项目所在的矩形。一个简单的4部分检查看看我们是否在那个盒子里。

    如果是这样,我们想要通过简单的减法实现相对于矩形的坐标,然后检查位图是否实际上是可点击的......那就是它!如果没有,我们认为它是一个错过并继续循环。如果是这样,我切换showing课程来完成剩下的工作。

    此代码应该可以在IE9 +浏览器上可靠地运行,而无需任何插件或库脚本。

    如果您对我的方法/代码有任何疑问,请与我们联系。

    编辑:大声笑我打电话给#34;造船厂&#34;但切断了发电厂的形象。哦,你知道我的意思。