如何在Google Maps API v3中使用SVG标记

109

我可以使用转换后的 image.svg 文件作为 Google 地图标志吗?我将我的 png 图像转换为 svg,并希望像 Google 地图符号那样使用它,可以旋转。我已经尝试使用了 Google 地图符号,但我想要像汽车、人等这样的标志...这就是为什么我将一些 png 文件转换为 svg 的原因,就像这个示例站点一样,他用什么东西来制作这些http://www.goprotravelling.com/


1
对于那些寻找类似问题的人:这里有一个部分兼容IE 9+的解决方案:https://dev59.com/Yl4d5IYBdhLWcg3wCOyD#30890373,它会将SVG放置在DOM中而不是画布上,这样您就可以在之后使用CSS3进行操作(如果您想要在运行时特别旋转该元素)。 - Mike Kormendy
如果您的地图上有许多标记,建议使用png光栅化标记。https://cloud.google.com/blog/products/maps-platform/google-maps-platform-best-practices-optimization-and-performance-tips - Adam
8个回答

160

您可以使用SVG路径符号来呈现您的图标。

有关更多信息,请参见谷歌文档

以下是一个基本示例:

var icon = {

    path: "M-20,0a20,20 0 1,0 40,0a20,20 0 1,0 -40,0",
    fillColor: '#FF0000',
    fillOpacity: .6,
    anchor: new google.maps.Point(0,0),
    strokeWeight: 0,
    scale: 1
}

var marker = new google.maps.Marker({
    position: event.latLng,
    map: map,
    draggable: false,
    icon: icon
});

以下是如何显示和缩放标记SVG图标的工作示例:

JSFiddle演示

编辑:

这里还有一个带有复杂图标的示例:

JSFiddle演示

编辑2:

这里是如何将SVG文件用作图标的方法:

JSFiddle演示


1
制作汽车 SVG 非常困难。 - jemz
你有那辆车的SVG图像吗?那么只需用文本编辑器打开它并找到其中的路径! - MrUpsidown
3
SVG通常用于图标和其他符号,只有一种颜色。它就像字体中的字母一样,只是一个路径。优点是您可以轻松地缩放、旋转和更改其颜色,而不会失去质量。这是一个矢量形状。 - MrUpsidown
@MrUpsidown,我该如何像您在复杂示例中所做的那样,将复杂的SVG图像分解为路径符号?特别是当我有嵌套在路径内部的路径时? - E.E.33
@E.E.33 有相应的工具可供使用。Inkscape, Illustrator 可能会有所帮助。另外,尝试使用文本编辑器(如记事本、Sublime text等)打开你的 SVG 文件,你将会发现其中的内容。 ;) - MrUpsidown

86

如果您需要的是完整的SVG图像而不仅仅是路径,并且希望可以在客户端上进行修改(例如更改文本,隐藏细节等),则可以使用包含SVG的备用数据“URL”:

var svg = '<svg width="400" height="110"><rect width="300" height="100" /></svg>';
icon.url = 'data:image/svg+xml;charset=UTF-8;base64,' + btoa(svg);

JavaScript (Firefox) 的 btoa() 函数用于从 SVG 文本中获取 base64 编码。您也可以使用http://dopiaza.org/tools/datauri/index.php 生成 base data URL。

这里是一个完整的示例 jsfiddle

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
        <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
    </head>
    <body>
        <div id="map" style="width: 500px; height: 400px;"></div>
        <script type="text/javascript">
            var map = new google.maps.Map(document.getElementById('map'), {
                zoom: 10,
                center: new google.maps.LatLng(-33.92, 151.25),
                mapTypeId: google.maps.MapTypeId.ROADMAP
            });

            var template = [
                '<?xml version="1.0"?>',
                    '<svg width="26px" height="26px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">',
                        '<circle stroke="#222" fill="{{ color }}" cx="50" cy="50" r="35"/>',
                    '</svg>'
                ].join('\n');
            var svg = template.replace('{{ color }}', '#800');

            var docMarker = new google.maps.Marker({
                position: new google.maps.LatLng(-33.92, 151.25),
                map: map,
                title: 'Dynamic SVG Marker',
                icon: { url: 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg), scaledSize: new google.maps.Size(20, 20) },
optimized: false
            });

            var docMarker = new google.maps.Marker({
                position: new google.maps.LatLng(-33.95, 151.25),
                map: map,
                title: 'Dynamic SVG Marker',
                icon: { url: 'data:image/svg+xml;charset=UTF-8;base64,' + btoa(svg), scaledSize: new google.maps.Size(20, 20) },
optimized: false
            });
        </script>
    </body>
</html>

可在此处找到其他信息:这里

避免使用base64编码:

为了避免使用base64编码,您可以将'data:image/svg+xml;charset=UTF-8;base64,' + btoa(svg)替换为'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg)

这应该适用于从 IE9 到现代浏览器。 优点是encodeURIComponent是默认的js函数,并且在所有现代浏览器中都可用。您可能还会获得更小的链接,但需要测试并考虑在svg中使用'而不是"

还请参见数据URI中的SVG优化获取更多信息。

IE支持: 为了支持IE中的SVG标记,需要两个小修改,如此处所述:IE中的SVG标记。我更新了示例代码以支持IE。


3
信息不错,但需要指出的是,base64编码实际上并不是必要或理想的。可以直接在数据URI中使用svg编码,比如data:image/svg+xml;utf8,。更多信息请参考:https://css-tricks.com/probably-dont-base64-svg/。 - Josh from Qaribou
你说得对,如果能避免使用base64编码那就太好了。你是如何实现的呢?可以给我提供一个例子吗?如果我只是将svg代码添加到URI中,它不能与一些字符(例如#)一起工作。你是否使用了URL编码? - Tom
我应该提到它是utf8编码的;svg是格式。提供的链接有几个示例。 - Josh from Qaribou
1
如何使用它旋转SVG图像? - Somnath Kharat
我们难道不都喜欢IE吗 ;) 像往常一样,需要额外的工作才能使它在所有浏览器上正常工作。我改编了这个例子以支持IE,希望这有所帮助。 - Tom
1
谷歌地图团队使用相同的方法来创建默认图标,例如街景、放大、缩小、全屏等。 - charlieb

37

我知道这篇文章有点旧了,但是在SO上,我看到了太多关于此事的错误信息,以至于我都快要尖叫了。因此,我必须用自己知道行得通的全新方法来发表我的见解,因为我在许多地图上都可靠地使用它。除此之外,我认为OP想要的不仅是能够围绕地图点旋转箭头标记,而且还包括围绕其自身的x、y轴旋转图标,这将改变箭头标记指向地图的位置。

首先,记住我们正在玩谷歌地图和SVG,所以我们必须适应谷歌部署SVG标记(或实际上是符号)的方式。谷歌将其SVG标记图像的锚点设置为0,0,这不是SVG viewBox的左上角。为了克服这个问题,您必须稍微以一种不同的方式绘制SVG图像,以给予谷歌所需的内容...... 是的,答案就在您实际在SVG编辑器(Illustrator,Inkscape等)中创建SVG路径的方式中。

第一步是摆脱viewBox。这可以通过将XML中的viewBox设置为0来完成...没错,只需要一个零,而不是viewBox的常规四个参数。这将关闭viewBox(是的,这在语义上是正确的)。当您执行此操作时,可能会注意到图像的大小立即跳跃,这是因为SVG不再具有基础(viewBox)来缩放图像。因此,我们直接创建该引用,通过在SVG编辑器的XML编辑器中将宽度和高度设置为您希望图像实际显示的像素数。

通过在XML编辑器中设置svg图像的宽度和高度,您可以创建图像缩放的基准线,并且该大小默认情况下成为标记比例属性的值1。您可以看到它对于标记的动态缩放有什么优势。

现在,您已经调整了图像大小,请将图像移动到您希望作为锚点的图像部分覆盖svg编辑器的0,0坐标。一旦您这样做了,请复制svg路径的d属性的值。您会注意到约一半的数字是负数,这是将锚点对齐为图像的0,0而不是viewBox的结果。

使用这种技术将使您能够正确地旋转标记,围绕地图上的纬度和经度点。这是将要素点与标记位置的纬度和经度绑定的唯一可靠方法。

我尝试创建了一个JSFiddle,但是其中有一些错误,这也是我不太喜欢重新解释代码的原因之一。因此,我在下面包含了一个完全自包含的示例,您可以尝试、适应并用作参考。这是我在JSFiddle上尝试过但失败的相同代码,但它在Firebug中完全通过。

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <meta name="viewport"  content="width=device-width, initial-scale=1" />
    <meta name="author"    content="Drew G. Stimson, Sr. ( Epiphany )" />
    <title>Create Draggable and Rotatable SVG Marker</title>
    <script src="http://maps.googleapis.com/maps/api/js?sensor=false"> </script>
    <style type="text/css">
      #document_body {
        margin:0;
        border: 0;
        padding: 10px;
        font-family: Arial,sans-serif;
        font-size: 14px;
        font-weight: bold;
        color: #f0f9f9;
        text-align: center;
        text-shadow: 1px 1px 1px #000;
        background:#1f1f1f;
      }
      #map_canvas, #rotation_control {
        margin: 1px;
        border:1px solid #000;
        background:#444;
        -webkit-border-radius: 4px;
           -moz-border-radius: 4px;
                border-radius: 4px;
      }
      #map_canvas { 
        width: 100%;
        height: 360px;
      }
      #rotation_control { 
        width: auto;
        padding:5px;
      }
      #rotation_value { 
        margin: 1px;
        border:1px solid #999;
        width: 60px;
        padding:2px;
        font-weight: bold;
        color: #00cc00;
        text-align: center;
        background:#111;
        border-radius: 4px;
      }
</style>

<script type="text/javascript">
  var map, arrow_marker, arrow_options;
  var map_center = {lat:41.0, lng:-103.0};
  var arrow_icon = {
    path: 'M -1.1500216e-4,0 C 0.281648,0 0.547084,-0.13447 0.718801,-0.36481 l 17.093151,-22.89064 c 0.125766,-0.16746 0.188044,-0.36854 0.188044,-0.56899 0,-0.19797 -0.06107,-0.39532 -0.182601,-0.56215 -0.245484,-0.33555 -0.678404,-0.46068 -1.057513,-0.30629 l -11.318243,4.60303 0,-26.97635 C 5.441639,-47.58228 5.035926,-48 4.534681,-48 l -9.06959,0 c -0.501246,0 -0.906959,0.41772 -0.906959,0.9338 l 0,26.97635 -11.317637,-4.60303 c -0.379109,-0.15439 -0.812031,-0.0286 -1.057515,0.30629 -0.245483,0.33492 -0.244275,0.79809 0.0055,1.13114 L -0.718973,-0.36481 C -0.547255,-0.13509 -0.281818,0 -5.7002158e-5,0 Z',
    strokeColor: 'black',
    strokeOpacity: 1,
    strokeWeight: 1,
    fillColor: '#fefe99',
    fillOpacity: 1,
    rotation: 0,
    scale: 1.0
  };

function init(){
  map = new google.maps.Map(document.getElementById('map_canvas'), {
    center: map_center,
    zoom: 4,
    mapTypeId: google.maps.MapTypeId.HYBRID
  });
  arrow_options = {
    position: map_center,
    icon: arrow_icon,
    clickable: false,
    draggable: true,
    crossOnDrag: true,
    visible: true,
    animation: 0,
    title: 'I am a Draggable-Rotatable Marker!' 
  };
  arrow_marker = new google.maps.Marker(arrow_options);
  arrow_marker.setMap(map);
}
function setRotation(){
  var heading = parseInt(document.getElementById('rotation_value').value);
  if (isNaN(heading)) heading = 0;
  if (heading < 0) heading = 359;
  if (heading > 359) heading = 0;
  arrow_icon.rotation = heading;
  arrow_marker.setOptions({icon:arrow_icon});
  document.getElementById('rotation_value').value = heading;
}
</script>
</head>
<body id="document_body" onload="init();">
  <div id="rotation_control">
    <small>Enter heading to rotate marker&nbsp;&nbsp;&nbsp;&nbsp;</small>
    Heading&deg;<input id="rotation_value" type="number" size="3" value="0" onchange="setRotation();" />
    <small>&nbsp;&nbsp;&nbsp;&nbsp;Drag marker to place marker</small>
    </div>
    <div id="map_canvas"></div>
</body>
</html>

这正是谷歌为其 Maps API 的 SYMBOL 类中的几个符号所做的,所以如果这还不能说服您......

无论如何,我希望这能帮助您正确地创建和设置 Google 地图上的 SVG 标记。


3
很好的回答,我已经为大家制作了一个工作的fiddle来展示你的代码http://jsfiddle.net/v2pjfp7r/ - Rob Schmuecker
1
"<3" 这是必要的信息,应该包含在谷歌文档中。 - user7653815
这就是我需要的信息!如果您在Illustrator中创建SVG(在上面关于viewBox的建议之后),请确保使用“另存为...”然后将其保存为SVG,“导出屏幕...”无法正常工作。 - Phil
这是完美的答案 - 帮助我解决了一个问题,其中标记在缩放时漂移。原来是由于锚点没有设置在正确的坐标系中引起的。 - 0xCC

13

情况正在好转,现在您可以使用SVG文件。

        marker = new google.maps.Marker({
            position: {lat: 36.720426, lng: -4.412573},
            map: map,
            draggable: true,
            icon: "img/tree.svg"
        });

3
不允许旋转。 - Matt Westlake

13

是的,您可以像使用.png或其他图像文件格式一样使用.svg文件作为图标。只需将图标的URL设置为.svg文件所在的目录即可。例如:

var icon = {
        url: 'path/to/images/car.svg',
        size: new google.maps.Size(sizeX, sizeY),
        origin: new google.maps.Point(0, 0),
        anchor: new google.maps.Point(sizeX/2, sizeY/2)
};

var marker = new google.maps.Marker({
        position: event.latLng,
        map: map,
        draggable: false,
        icon: icon
});

1
你正在展示的图标对象是一个标准标记,而不是SVG标记。SVG标记使用符号类的属性,然后将其分配为标记类的图标属性。(请参见MrUpsidown图标对象以获得正确的参考...) - Epiphany
2
如果SVG具有多个“d”路径(通常与SVG图像中的多个层相关联),这也会出现问题。 - Epiphany
2
请注意,您的SVG文件应明确设置图标的尺寸,如下所示:width="33px" height="50px" - 请参见https://dev59.com/z2865IYBdhLWcg3wLrhE#21783089。 - Yarin

8

正如本帖中其他人所提到的,不要忘记在 SVG 中显式设置 宽度和高度属性,就像这样:

<svg id="some_id" data-name="some_name" xmlns="http://www.w3.org/2000/svg"
     viewBox="0 0 26 42"
     width="26px" height="42px">

如果您不这样做,没有任何 JavaScript 操作可以帮助您,因为Gmaps将没有参考框架并始终使用标准大小。

(我知道某些评论中已经提到了这一点,但它们很容易被忽略。这个信息在各种情况下都对我有所帮助)


0

好的!我很快就在我的网站上完成了这个,我尝试了两种方法来创建自定义的谷歌地图标记,使用canvg.js运行的代码是最兼容浏览器的。注释掉的代码目前不支持IE11。

var marker;
var CustomShapeCoords = [16, 1.14, 21, 2.1, 25, 4.2, 28, 7.4, 30, 11.3, 30.6, 15.74, 25.85, 26.49, 21.02, 31.89, 15.92, 43.86, 10.92, 31.89, 5.9, 26.26, 1.4, 15.74, 2.1, 11.3, 4, 7.4, 7.1, 4.2, 11, 2.1, 16, 1.14];

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 13,
    center: {
      lat: 59.325,
      lng: 18.070
    }
  });
  var markerOption = {
    latitude: 59.327,
    longitude: 18.067,
    color: "#" + "000",
    text: "ha"
  };
  marker = createMarker(markerOption);
  marker.setMap(map);
  marker.addListener('click', changeColorAndText);
};

function changeColorAndText() {
  var iconTmpObj = createSvgIcon( "#c00", "ok" );
  marker.setOptions( {
                icon: iconTmpObj
            } );
};

function createMarker(options) {
  //IE MarkerShape has problem
  var markerObj = new google.maps.Marker({
    icon: createSvgIcon(options.color, options.text),
    position: {
      lat: parseFloat(options.latitude),
      lng: parseFloat(options.longitude)
    },
    draggable: false,
    visible: true,
    zIndex: 10,
    shape: {
      coords: CustomShapeCoords,
      type: 'poly'
    }
  });

  return markerObj;
};

function createSvgIcon(color, text) {
  var div = $("<div></div>");

  var svg = $(
    '<svg width="32px" height="43px"  viewBox="0 0 32 43" xmlns="http://www.w3.org/2000/svg">' +
    '<path style="fill:#FFFFFF;stroke:#020202;stroke-width:1;stroke-miterlimit:10;" d="M30.6,15.737c0-8.075-6.55-14.6-14.6-14.6c-8.075,0-14.601,6.55-14.601,14.6c0,4.149,1.726,7.875,4.5,10.524c1.8,1.801,4.175,4.301,5.025,5.625c1.75,2.726,5,11.976,5,11.976s3.325-9.25,5.1-11.976c0.825-1.274,3.05-3.6,4.825-5.399C28.774,23.813,30.6,20.012,30.6,15.737z"/>' +
    '<circle style="fill:' + color + ';" cx="16" cy="16" r="11"/>' +
    '<text x="16" y="20" text-anchor="middle" style="font-size:10px;fill:#FFFFFF;">' + text + '</text>' +
    '</svg>'
  );
  div.append(svg);

  var dd = $("<canvas height='50px' width='50px'></cancas>");

  var svgHtml = div[0].innerHTML;

  canvg(dd[0], svgHtml);

  var imgSrc = dd[0].toDataURL("image/png");
  //"scaledSize" and "optimized: false" together seems did the tricky ---IE11  &&  viewBox influent IE scaledSize
  //var svg = '<svg width="32px" height="43px"  viewBox="0 0 32 43" xmlns="http://www.w3.org/2000/svg">'
  //    + '<path style="fill:#FFFFFF;stroke:#020202;stroke-width:1;stroke-miterlimit:10;" d="M30.6,15.737c0-8.075-6.55-14.6-14.6-14.6c-8.075,0-14.601,6.55-14.601,14.6c0,4.149,1.726,7.875,4.5,10.524c1.8,1.801,4.175,4.301,5.025,5.625c1.75,2.726,5,11.976,5,11.976s3.325-9.25,5.1-11.976c0.825-1.274,3.05-3.6,4.825-5.399C28.774,23.813,30.6,20.012,30.6,15.737z"/>'
  //    + '<circle style="fill:' + color + ';" cx="16" cy="16" r="11"/>'
  //    + '<text x="16" y="20" text-anchor="middle" style="font-size:10px;fill:#FFFFFF;">' + text + '</text>'
  //    + '</svg>';
  //var imgSrc = 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);

  var iconObj = {
    size: new google.maps.Size(32, 43),
    url: imgSrc,
    scaledSize: new google.maps.Size(32, 43)
  };

  return iconObj;
};
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Your Custom Marker </title>
  <style>
    /* Always set the map height explicitly to define the size of the div
       * element that contains the map. */
    #map {
      height: 100%;
    }
    /* Optional: Makes the sample page fill the window. */
    html,
    body {
      height: 100%;
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <div id="map"></div>
    <script src="https://canvg.github.io/canvg/canvg.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>
</body>

</html>


-5

你需要传递 optimized: false

例如:

var img = { url: 'img/puff.svg', scaledSide: new google.maps.Size(5, 5) };
new google.maps.Marker({position: this.mapOptions.center, map: this.map, icon: img, optimized: false,});

如果不传递 optimized: false,我的 SVG 将显示为静态图像。


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