谷歌地图API多图标标记

5
我希望使用Google Maps JavaScript API实现一个复杂的标记,它结合了静态图像(PNG文件)和可编辑的“即时”图像,例如SVG。这是我的目标:

Google Maps Marker PNG + SVG

我已经将"士兵"作为PNG文件标记构建好,一切都很顺利。然而,我似乎找不到一种方法来将多个元素添加到标记的"icon"属性中。我已经阅读了所有Google Maps API文档、参考和示例,并查看了这个链接: 如何在v3中为Google地图标记放置多个图标? 那个链接似乎并没有帮助,因为我想让士兵下面的红条随着代表士兵健康状况的动态变化而改变。
有人有可行的解决方案吗?我不介意使用CSS甚至Canvas,只要它呈现一个简单的矩形,其长度可以动态调整。
谢谢。

为什么获得了负投票? - Arj
为什么不直接使用两个标记?如果您需要它们一起移动,请在位置上使用bindTo。 - geocodezip
嗯,谢谢geocodezip,这是一个有趣的想法。我会试一下看看会发生什么。 - Arj
2个回答

3

一种选择是使用两个分别绑定在一起的标记(如果您需要移动它们):

var soldier = new google.maps.Marker({
  position: map.getCenter(),
  map: map,
  draggable: true,
  icon: { url: "http://www.geocodezip.com/mapIcons/greenSoldier.png",
   scaledSize: new google.maps.Size(32,48)
  }
});
var health = new google.maps.Marker({
  position: soldier.getPosition(),
  map: map,
  icon: { path: "M 100 100 L 150 100 L 150 110 L 100 110 z",
  scale: 0.5,
  fillColor: "red",
  fillOpacity: 1.0,
  strokeColor: "black",
  strokeWeight: 2,
  anchor: new google.maps.Point(125,110)
  }
});
health.bindTo("position", soldier);

概念证明 Fiddle

生成地图的截图

代码片段:

function initialize() {
  var map = new google.maps.Map(
    document.getElementById("map_canvas"), {
      center: new google.maps.LatLng(37.4419, -122.1419),
      zoom: 13,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });
  var soldier = new google.maps.Marker({
    position: map.getCenter(),
    map: map,
    draggable: true,
    icon: {
      url: "https://www.geocodezip.net/mapIcons/greenSoldier.png",
      scaledSize: new google.maps.Size(32, 48)
    }
  });
  var health = new google.maps.Marker({
    position: soldier.getPosition(),
    map: map,
    icon: {
      path: "M 100 100 L 150 100 L 150 110 L 100 110 z",
      scale: 0.5,
      fillColor: "green",
      fillOpacity: 1.0,
      strokeColor: "black",
      strokeWeight: 2,
      anchor: new google.maps.Point(125, 110)
    }
  });
  health.bindTo("position", soldier);
  setInterval(changeLength, 5000);
  var idx = 0;

  function changeLength() {
    var icon = health.getIcon();
    icon.fillColor = colors[(idx) % colors.length];
    icon.path = lengths[(idx++) % lengths.length];
    health.setIcon(icon);
  }

}
google.maps.event.addDomListener(window, "load", initialize);
var colors = ["green", "green", "yellow", "red"];
var lengths = ["M 100 100 L 150 100 L 150 110 L 100 110 z",
  "M 100 100 L 140 100 L 140 110 L 100 110 z",
  "M 100 100 L 130 100 L 130 110 L 100 110 z",
  "M 100 100 L 120 100 L 120 110 L 100 110 z"
]
html,
body,
#map_canvas {
  height: 100%;
  width: 100%;
  margin: 0px;
  padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="map_canvas"></div>


嗨,geocodezip,非常感谢您详细的回复。根据您在我的原始问题下的评论,我仍在尝试绑定标记。我遇到了一些问题,即基于我添加到Google标记的自定义健康属性触发更改健康条的大小(即每次士兵受到打击时,减少他的健康状况,从而减少健康条)。我使用自定义画布标记取得了一些成功,但我也会尝试您的方法,并在此发布我的结果和代码,以防其他人将来需要类似的东西。 - Arj

0

如承诺的那样,我已经包含了我的完整、可工作的代码。这个函数将标记绘制为画布图像。士兵和他的生命条分别绘制,因为士兵通常是静态的(除非我们想通过画布来动画化他),但当他受到攻击时,生命条会发生变化。我已经在我的游戏中测试了代码,它完美地工作。

geocodezip,出于性能原因,我最终没有采用您的建议,主要原因是不想将游戏中的标记数量翻倍。一个玩家可以在游戏中拥有数百个单位,而任何时候都可能有数百名玩家,因此为了实现我想要的效果而翻倍标记可能会影响性能。不过,您的代码很棒,我可能会在不会影响性能的情况下使用它。

以下是代码:

function createUnitMarker(latLng, unitData) {
    var img = new Image();
    img.crossOrigin = "Anonymous";
    img.src = 'http://yourservername.com/' + unitData.icon;

    img.onload = function() {
        // google maps markers can only be created inside the onload function, otherwise nothing is drawn

        // source: GitHub, viktorkelemen/Custom canvas google maps marker
        // create the marker and all associated elements
        var canvas, context;
        var color = "blue";
        var healthBar = { // this object controls aspects of the unit health bar
            width: 62,
            height: 10,
            x: 5,
            y: 82
        }

        canvas = document.createElement("canvas");
        context = canvas.getContext("2d");

        canvas.width = unitData.width;
        canvas.height = unitData.height;

        function drawBar(percent, color) {
            // this function draws the unit health bar

            // colours the health bar background grey
            context.fillStyle = "gray";
            context.fillRect(healthBar.x, healthBar.y, healthBar.width, healthBar.height);

            // fills the health bar with the passed colour, or empty it if the unit dies
            if (typeof percent == undefined || percent == null || percent == NaN) {
                percent = 100;
            }

            if (typeof color == undefined || color == null || color == NaN) {
                color = "green";
            }

            // we want to ensure that the health bar fill is always contained within the health bar width
            var width = Math.min(percent * healthBar.width, healthBar.width);
            width = Math.max(width, 0);

            // colour-code the health bar depending on health
            if (percent < 0.4) {
                color = "red";
            }

            else if (percent < 0.7) {
                color = "orange";
            }

            else {
                color = "green";
            }

            context.fillStyle = color;
            context.fillRect(healthBar.x, healthBar.y, width, healthBar.height);

            // places a black stroke around the health bar for greater visibility
            context.lineWidth = 2;
            context.strokeStyle = "black";
            context.strokeRect(healthBar.x, healthBar.y, healthBar.width, healthBar.height);
        }

        function drawIcon(img, width, height) {
            // this function draws the unit

            if (typeof img === "undefined" || img === null || img === "" || img === NaN) {
                img = img;
            }

            if (typeof width === "undefined" || width === null || width === "" || width === NaN) {
                width = unitData.width;
            }

            if (typeof height === "undefined" || height === null || height === "" || height === NaN) {
                height = unitData.height;
            }

            context.clearRect(0, 0, width, height);

            // save the canvas context
            context.save();

            // draw the image
            context.drawImage(img, 0, 0);

            context.restore();
        }

        drawIcon(img, unitData.width, unitData.height);

        // create an icon based on the png we want to use
        var icon = {
            url: canvas.toDataURL(),
            size: new google.maps.Size(unitData.width, unitData.height), // the dimensions in pixels of the marker image
            origin: new google.maps.Point(0, 0), // the origin for this image, where top left is (0, 0)
            labelOrigin: new google.maps.Point(25, 40), // the position of the label relative to the icon
            anchor: new google.maps.Point(unitData.width / 2, unitData.height / 2) // the anchor for this image, or where the "base point" is located
        }

        // this is the key unit object. it should store all information directly related to this unit
        var marker = new google.maps.Marker({
            position: latLng, // the lat/lon where the marker is drawn
            label: {
                color: "yellow",
                fontFamily: "Oswald",
                fontSize: "20px",
                fontWeight: "bold", // use CSS font-weight properties
                text: unitData.unit_ID.toString(),
                visibility: "hidden"
            },
            title: "Infantry", // title on hover
            icon: icon, // the icon to be drawn, using an image file or canvas
            animation: google.maps.Animation.DROP, // a bit of flair :)
            // every element below this point is game-specific and not part of the google maps api
            unitID: unitData.unit_ID, // the unique unit id for this unit as returned by the server
            etc: 0
        });

        // to add the marker to the map, call setMap()          
        marker.setMap(gv.map);

        // draw the health bar last so that it's always on top, and after we know the unit's current/max health
        var healthPercent = marker.current_health / marker.max_health;
        drawBar(healthPercent);

        // these are the key commands required every time we need to re-draw something on the unit
        icon.url = canvas.toDataURL(); // re-draws the canvas, usually because we have changed something above this line
        icon = JSON.parse(JSON.stringify(icon)); // stringifies the icon properties
        marker.setIcon(icon); // applies the changed icon properties to the icon

        marker.addListener('label_changed', function() {
            // this listener is intended to update the unit's health bar on attack. it's a bit of a hack as it relies on changes to the marker's label to fire an event. other options are changing a marker's title, or writing a custom event (which is more involved)
            // draw the health bar last so that it's always on top, and after we know the unit's current/max health
            healthPercent = this.current_health / this.max_health;
            drawBar(healthPercent);

            icon = {
                url: canvas.toDataURL(),
                size: new google.maps.Size(unitData.width, unitData.height), // the dimensions in pixels of the marker image
                origin: new google.maps.Point(0, 0), // the origin for this image, where top left is (0, 0)
                labelOrigin: new google.maps.Point(25, 40), // the position of the label relative to the icon
                anchor: new google.maps.Point(unitData.width / 2, unitData.height / 2) // the anchor for this image, or where the "base point" is located
            }

            // all we want to do is change the icon url, but we can't just pass it to setIcon as it will lose the other icon properties. instead, we clone the icon object and change the url in the clone, passing it to setIcon when selected
            icon = JSON.parse(JSON.stringify(icon));

            this.setIcon(icon);
        });
    }
}

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