SVG单击处理与显示的位置不一致

时间:2014-11-29 20:39:01

标签: javascript jquery html ruby-on-rails svg

我正在建立一个基于网络的世界统治游戏。游戏的主干是一个由三十八个地区组成的三层可缩放地图。

Zooming Map

我使用SVG路径构建了地图。全局视图具有宽屏宽高比。在方形屏幕上显示地图时,为了美观,整个SVG必须使用动画标签垂直居中。我已经完美地完成了工作,当我点击缩放地图时,我不理解的部分就出现了。似乎为了点击的目的,地图是显示在其上方的几个(大约25)像素。例如,如果我点击俄罗斯上方的灰色背景,它会让我变成亚洲大陆。当我考虑到我在领土和大陆缩放级别的每条路径上使用变换这一事实时,我似乎更有趣,并且不会发生同样的问题。

HTML看起来像这样:

<div class="test1"  width="100%" height="100%">
    <div id="renderedSprites"></div>
    <div id="background"></div>
    <div id="continentFilter" class="filter"></div>
    <div id="territoryFilter" class="filter"></div>
    <svg
    preserveAspectRatio="xMinYMin meet"
    class="territoryImage" 
    id="continentalSVG"></svg>
    <svg
    preserveAspectRatio="xMinYMin meet"
    class="territoryImage" 
    id="territorialSVG"></svg>
    <%= render partial: "images" %>
</div>

图像部分包含全局SVG,它太长而无法显示,但遵循以下格式:

 <svg
    preserveAspectRatio="xMinYMin meet"
    class="territoryImage" 
    id="globalSVG">

   <g>
  <path
 class="continentName"
   id="territoryName"

     d="..."

     inkscape:connector-curvature="0" />
 </g>

37 More paths and animations...

</svg>

缩放JS很简单,几乎无关紧要。我只是将所需的地图片段的路径克隆到该级别的正确SVG中。然后,我在更高的z-index处显示该SVG,并在显示的级别之间放置一个过滤器或一组过滤器。但是,无论如何(对不起,它如此包装和恶心):

var zoomLevel = ["global","globe"];
//renderObject("encampment",zoomLevel);
//renderObject("fort",zoomLevel);
$(".territoryImage").click( function(e) {
if ($(document.elementFromPoint(e.pageX,e.pageY)).is("path") == true) {
    if (zoomLevel[0] == "global") { 
        var continent = $(".".concat($(document.elementFromPoint(e.pageX,e.pageY)).attr("class")))
        for (i = 0; i < continent.length; i++) {
            $("#continentalSVG").append($(continent[i]).parent().clone())
        }

        var box = $("#continentalSVG")[0].getBoundingClientRect()
        var newScaleFactor
        if (box.width/$(window).width() > box.height/$(window).height()) {
            newScaleFactor = ($("#background").width()*parseFloat($("#continentalSVG").children("g").attr("transform").split(")")[0].replace("scale(","")))/box.width*0.95;
            console.log(newScaleFactor);
        }
        else {
            newScaleFactor = ($("#background").height()*parseFloat($("#continentalSVG").children("g").attr("transform").split(")")[0].replace("scale(","")))/box.height*0.95;
            console.log(newScaleFactor);
        }
        $("#continentalSVG").children("g").attr("transform","scale(" + newScaleFactor + ")");
        var scaledBox = $("#continentalSVG").offset();
        scaledBox.top = ((scaledBox.top - $("#background").offset().top)/newScaleFactor) - (($("#background").height()/2) - ($("#continentalSVG")[0].getBoundingClientRect().height/2))
        scaledBox.left = ((scaledBox.left - $("#background").offset().left)/newScaleFactor) - (($("#background").width()/2) - ($("#continentalSVG")[0].getBoundingClientRect().width/2))
        $("#continentalSVG").children("g").attr("transform", $("#continentalSVG").children("g").attr("transform") + " translate(" + -1*scaledBox.left + ", " + -1*scaledBox.top + ")")
        $("#continentalSVG").css("z-index","2");
        $("#continentFilter").css("display","inline");
        zoomLevel = ["continental",$("#continentalSVG").children("g").attr("class")];
        //renderObject("encampment",zoomLevel);
        //renderObject("fort",zoomLevel);
    }
    else if (zoomLevel[0] == "continental") {
        var territory = "#" + document.elementFromPoint(e.pageX,e.pageY).id;
        $("#territorialSVG").append($(territory).parent().clone())
        var box = $("#territorialSVG")[0].getBoundingClientRect();
        var newScaleFactor
        if (box.width/$(window).width() > box.height/$(window).height()) {
            newScaleFactor = ($("#background").width()*parseFloat($("#territorialSVG").children("g").attr("transform").split(")")[0].replace("scale(","")))/box.width*0.95;
        }
        else {
            newScaleFactor = ($("#background").height()*parseFloat($("#territorialSVG").children("g").attr("transform").split(")")[0].replace("scale(","")))/box.height*0.95;
        }
        $("#territorialSVG").children("g").attr("transform","scale(" + newScaleFactor + ")");
        var scaledBox = $("#territorialSVG").offset()
        //console.log(scaledBox)
        scaledBox.top = ((scaledBox.top - $("#background").offset().top)/newScaleFactor) - ($("#background").height() - ($("#territorialSVG")[0].getBoundingClientRect().height))/(newScaleFactor*2)
        scaledBox.left = ((scaledBox.left - $("#background").offset().left)/newScaleFactor) - ($("#background").width() - ($("#territorialSVG")[0].getBoundingClientRect().width))/(newScaleFactor*2)
        //console.log(($("#background").width() - ($("#territorialSVG")[0].getBoundingClientRect().width))/(newScaleFactor*2))
        $("#territorialSVG").children("g").attr("transform", $("#territorialSVG").children("g").attr("transform") + " translate(" + -1*scaledBox.left + ", " + -1*scaledBox.top + ")")
        $("#territorialSVG").css("z-index","4");
        $("#territoryFilter").css("display","inline");
        zoomLevel = ["territorial",territory, zoomLevel[1]];
        //renderObject("encampment",zoomLevel);
        //renderObject("fort",zoomLevel);
    }
}

else {
    if (zoomLevel[0] == "continental") {
        $("#continentalSVG").children().remove();
        $("#continentFilter").css("display","none");
        $("#continentalSVG").css("z-index","-1");
        zoomLevel = ["global","globe"];
        //renderObject("encampment",zoomLevel);
        //renderObject("fort",zoomLevel);
    }
    else if (zoomLevel[0] == "territorial") {
        $("#territorialSVG").children().remove();
        $("#territoryFilter").css("display","none");
        $("#territorialSVG").css("z-index","-1");
        $("#continentalSVG").css("z-index","2");
        zoomLevel = ["continental", zoomLevel[2]];
        //renderObject("encampment",zoomLevel);
        //renderObject("fort",zoomLevel);
    }
}
 });

以下是相关部分:

function setOriginalZoom() {

//this first part scales all the SVG's as well as the background, the filters, and a div I use for sprites
$(".territoryImage").attr("viewBox", "0 0 " + $(window).width()*0.95 + " " + $(window).height()*0.95);
$(".territoryImage").attr("width", $(".territoryImage").attr("viewBox").split(" ")[2]);
$(".territoryImage").attr("height", $(".territoryImage").attr("viewBox").split(" ")[3]);

$("#background").css("width", $(".territoryImage").width())
$("#renderedSprites").css("height", $(".territoryImage").height())
$("#renderedSprites").css("width", $(".territoryImage").width())
$("#background").css("height", $(".territoryImage").height())
$(".filter").css("width", $(".territoryImage").width())
$(".filter").css("height", $(".territoryImage").height())
$("g").attr("transform", "scale(" + $(".territoryImage").attr("viewBox").split(" ")[2]/3508 + ")")

//this part vertically centers the global SVG. The part that seems to trip things up.
$("#globalSVG").children("g").attr("transform", $("#globalSVG").children("g").attr("transform") + " translate(0 " + (($("#background").height()/2) - ($("#globalSVG")[0].getBoundingClientRect().height/2) + ($("#globalSVG").offset().top - $("#background").offset().top))/(($(".territoryImage").attr("viewBox").split(" ")[2]/3508)*2) + ")")
}

我在页面加载时调用此函数,它将所有显示的元素集中在一起。它还为全局视图缩放SVG。我注释掉的最后一点是写在地图的垂直中心。

我发现我在地图上点击点击的方式可能是导致错误的原因。这段代码包含在上面的缩放部分中,正是我所说的:

  $(".territoryImage").click( function(e) {
    if ($(document.elementFromPoint(e.pageX,e.pageY)).is("path") == true) {

我这样写是因为更简单的方式,

$("path").click( function() {...});
只要有人点击SVG,

就会触发。无论出于何种原因,该解决方案将路径注册为SVG的已定义大小(根据我设置为窗口大小百分比的高度和宽度属性),而另一个实现仅在路径的填充部分被触发时触发点击。

现在如果有人能解决这个问题,我会非常感激。如果您有任何疑问,请务必尽快回复。

谢谢,

-Alex

0 个答案:

没有答案