在Google地图V3上旋转图像/标记图像

32

我该如何在 Google Map V3 上旋转图像(标记图像)?

  • 有一个非常棒的 V2 示例在这里,正好做我所需的事情。 但是是为 GMap2 设计的! 它们使用旋转画布来实现。
  • 使用JS / JQuery旋转图像经常被使用,这方面有多个答案。但是如何将其应用于我的地图图像呢?
  • 其中一种方法是针对不同角度拥有不同的图像并在它们之间切换-这不是我想要的。 我想通过代码旋转,而不是拥有那么多图像。

备注:有类似的问题,但全部针对 V2 而非 V3 (就我所知)。 我需要 V3 的方法。

17个回答

20

我用的解决这个问题的JavaScript类如下:

var RotateIcon = function(options){
    this.options = options || {};
    this.rImg = options.img || new Image();
    this.rImg.src = this.rImg.src || this.options.url || '';
    this.options.width = this.options.width || this.rImg.width || 52;
    this.options.height = this.options.height || this.rImg.height || 60;
    var canvas = document.createElement("canvas");
    canvas.width = this.options.width;
    canvas.height = this.options.height;
    this.context = canvas.getContext("2d");
    this.canvas = canvas;
};
RotateIcon.makeIcon = function(url) {
    return new RotateIcon({url: url});
};
RotateIcon.prototype.setRotation = function(options){
    var canvas = this.context,
        angle = options.deg ? options.deg * Math.PI / 180:
            options.rad,
        centerX = this.options.width/2,
        centerY = this.options.height/2;

    canvas.clearRect(0, 0, this.options.width, this.options.height);
    canvas.save();
    canvas.translate(centerX, centerY);
    canvas.rotate(angle);
    canvas.translate(-centerX, -centerY);
    canvas.drawImage(this.rImg, 0, 0);
    canvas.restore();
    return this;
};
RotateIcon.prototype.getUrl = function(){
    return this.canvas.toDataURL('image/png');
};

像这样调用:

var marker = new google.maps.Marker({
    icon: {
        url: RotateIcon
            .makeIcon(
                'https://ru.gravatar.com/userimage/54712272/b8eb5f2d540a606f4a6c07c238a0bf40.png')
            .setRotation({deg: 92})
            .getUrl()
    }})

点击此处查看实时示例。


@ErDmKo:按照指定的方法尝试使用外部图片,但似乎无法正常工作。http://jsfiddle.net/fe9grwdf/42/ - Mrudang Vora
3
谢谢您提供这段代码片段。但请注意,浏览器是异步加载的,因此在图片加载之前调用getUrl()可能会出现空标记。为解决这个问题,应该将setRotationgetUrl包装在承诺(promises)中。 - Clint
很棒的解决方案,我只需要添加一个监听器来等待rImg.onload,然后canvas.drawImage才能正常工作。 - Duncan Lukkenaer
我应该如何在React中使用Promise来包装setRotation和getUrl方法?请帮助我。 - Chathuranga Kasthuriarachchi

11
我发现了两个Google MAP V3的扩展:infobox.jsmarkerwithlabel.js,它们都可以处理图像DOM元素作为内容,并且我可以通过jQuery image rotate插件旋转它。
即使在旋转后也不需要重新设置标记的图像。
编辑:如下面的问题/评论所述:
标签的扩展是必需的,因为它可以处理其他DOM元素。所以我可以添加任意HTML作为标签,对于我的特定情况,我添加了该图像。然后我使用旋转插件旋转这个图像(标签的子级)。因此,给图像分配一个id以便轻松访问它。实际上,我仅使用一个标签用于图像,另一个标签用于描述性文本。
编辑2:由于Stephan的评论中提到了DOM准备好的问题
在我的代码中,我发现以下行。这表明在旋转图像之前我强制绘制标签。
    if (!this._drawn) myImageLabel.draw(); // 1st time force a draw, otherwise rotating the image will fail because an asynchronously drawn object has not all tags in place
    if (this.heading != 0) this.rotateImage(this.heading, true);

编辑3:创建Infobox.js的代码示例

this._img = document.createElement('img');
... further manipulations of _img / Size / Id / ...
var planeImageLabelOptions = {
            content: this._img,
            disableAutoPan: true,
            boxStyle: planeImageLabelBoxStyle,
            pixelOffset: new google.maps.Size(-imgOffsetW / 2, -imgOffsetH / 2),
            closeBoxURL: "",
            position: latlng,
            zIndex: this.altitude < 0 ? 100 : this.altitude
 };
 var planeImageLabel = new InfoBox(planeImageLabelOptions);

我尝试使用markerwithlable.js来实现这个功能,但是jquery无法抓取对象。如果我设置labelClass:“label”,那么$('.label').rotate(30);就不起作用了...你到底是怎么让它旋转的?谢谢。 - user1051849
所以,您是将标记图标和标签放在标签内部,而不是提供一个标记图标和一个标签? - Stephan
那就是诀窍。需要标签扩展,因为它可以处理其他DOM元素。所以我可以将任意HTML添加为标签,在我的特定情况下我添加了图像。然后我使用rotate插件旋转这个图像(标签的子元素)。 - Horst Walter
2
你什么时候旋转图片?我的意思是,你是否像下面这样做?var marker = new MarkerWithLabel({ position: p, map: map, labelContent: "some html with "}); $("#myimg").rotate(35);因为我尝试过这种方式,但它不起作用(因为DOM还没有被修改以插入图像)。对此有什么提示吗?我正在使用jQM。(虽然也许所有这些都是不同的问题...) - Stephan
1
对于那些由于调用“旋转”太早而陷入旋转图标问题的人,我找到了一种方法,即在加载googlemaps时进行回调。请参见http://stackoverflow.com/questions/13947566/how-can-i-put-an-alert-after-loading-google-map - Jonathan Bibas
显示剩余7条评论

10

我也曾经费了很大劲才弄清楚如何旋转 PNG 标记。 我是用以下方法解决的。您可以创建许多具有相同自定义图像的标记,并旋转您想要旋转的特定标记。 希望对您有所帮助。

var id = 'my_marker_01';
var img_url = "../img/car.png";
var my_icon = img_url + "#" + id;
var marker = new google.maps.Marker({
    icon: my_icon,
    ...
});

var rotate = 45;
$(`img[src="${my_icon}"]`).css(
    {'-webkit-transform' : 'rotate('+ rotate +'deg)',
       '-moz-transform' : 'rotate('+ rotate +'deg)',
       '-ms-transform' : 'rotate('+ rotate +'deg)',
       'transform' : 'rotate('+ rotate +'deg)'}); 

7

我该如何在Google地图V3上旋转一张图片(标记图片)?

我曾遇到同样的问题,我使用以下代码解决了它:

var gmap;
NgMap.getMap(function(map){
    gmap = map;
});

我假设你有一个包含图标的变量,例如:

我假设您拥有一个包含图标的变量,例如:

var imagePath = 'img/customMarker.png';

首先,我们需要创建标记选项:
var markerOptions = {
    location: [x, y],
    title:'some text',
    draggable: true,
    .
    .
    .
    icon: imagePath
};

让我们创建一个标记:

var marker = new google.maps.Marker(markerOptions);

我们需要设置地图:

marker.setMap(map);

现在如果您想旋转图像,需要执行以下操作:
  • 将imagePath变量的值更改为“img/customMarker.png#yourId”
  • 使用CSS设置旋转值(例如使用JQuery)
让我们看看。
imagePath = 'img/customMarker.png#markerOne';

$('img[src="img/customMarker.png#markerOne"]').css({
     'transform': 'rotate(45deg)'
});

当然,您可以将其做得更加高级:
function rotateMarker(selector, degree){
     $('img[src="img/customMarker.png#'+selector+'"]').css({
         'transform': 'rotate('+degree+'deg)'
     });
}

并且你的呼叫:

rotateMarker('markerOne', 45);

希望这对你有所帮助。

以上是全部内容。


1
对不起,我忘记了一些东西:我们需要在 markerOptions 变量中添加一个额外的属性:var markerOptions = { location: [x, y], title:'some text', draggable: true, . . . icon: imagePath, optimized:false, additionalProperty: value }; - Jesus Gilberto Valenzuela
可能这不是安全的。由于某些操作正在异步进行,因此该图像可能尚未在地图中。 - Ethan
我发现唯一的问题是事后无法旋转该标记。https://stackoverflow.com/questions/68877756/google-maps-js-custom-marker-image - exceptionsAreBad
我已经找到了解决方案,可以解决使用此答案时的剪切问题。请查看我的解决方案:https://stackoverflow.com/questions/68877756/google-maps-js-custom-marker-image - exceptionsAreBad

5

我已经使用以下代码在v3中完成了旋转:

<canvas id="carcanvas" width="1" height="1"></canvas>


    if (document.getElementById('carcanvas').getContext) {
        var supportsCanvas = true;
    } else {
        var supportsCanvas = false;
    }

    var rImg = new Image();
    rImg.src='/images/cariconl.png';

    // Returns the bearing in radians between two points.
    function bearing( from, to ) {
        // Convert to radians.
        var lat1 = from.latRadians();
        var lon1 = from.lngRadians();
        var lat2 = to.latRadians();
        var lon2 = to.lngRadians();
        // Compute the angle.
        var angle = - Math.atan2( Math.sin( lon1 - lon2 ) * Math.cos( lat2 ), Math.cos( lat1 ) * Math.sin( lat2 ) - Math.sin( lat1 ) * Math.cos( lat2 ) * Math.cos( lon1 - lon2 ) );
        if ( angle < 0.0 )
            angle  += Math.PI * 2.0;
        if (angle == 0) {angle=1.5;}
        return angle;
    }

    function plotcar() {
        canvas = document.getElementById("carcanvas").getContext('2d');
        var cosa = Math.cos(angle);
        var sina = Math.sin(angle);
        canvas.clearRect(0,0,32,32);
        canvas.save();
        canvas.rotate(angle);
        canvas.translate(16*sina+16*cosa,16*cosa-16*sina);
        canvas.drawImage(rImg,-16,-16);
        canvas.restore();
    }

在动画方法中:
if (supportsCanvas) {
                    angle = bearing(new google.maps.LatLng(lat1, lng1),new google.maps.LatLng(lat2, lng2));
                    plotcar();
                }

我希望您能得到帮助。

你好,谢谢。我能旋转我的 PNG 图像,但是在围绕图像旋转后,它有黑色背景,如何设置透明背景? - Amir Farahani

5
没有人提到使用预先旋转的图标。根据您的应用程序,您可以取一个图标并将其旋转+10度、+20度……+350度,而不是旋转标记本身,只需为其分配不同的图标 - 如果10度分辨率足够好,则为36个中的一个。这对客户端资源也非常轻。
在下面的示例中,我生成了36个图标,每个图标都旋转了10度。它们的名称是:icon0.png,icon10.png,icon20.png,...icon340.png,icon350.png,icon360.png。0和360是完全相同的图标(例如符号链接)。
var rotation = 123 // degrees
var iconName = "icon" + (Math.round(rotation/10)*10).toString() + ".png"
var marker = new google.maps.Marker({
    icon: iconName
})

5
您没有在问题中说明,但我假设您希望围绕点a和点b之间的一条线进行旋转。为了制作一个可以旋转的谷歌svg图标,您需要使用谷歌符号类对象来定义标记符号的属性。这不使用完整的.svg文件,而只是路径的d属性。请注意,谷歌符号类只能接受每个标记一个路径。
可以在javascript中创建标记后设置颜色、描边、宽度、不透明度等其他属性(直接更新标记对象属性),或者使用CSS(通过添加和删除类更新标记属性)。
例如,以下内容将创建一个箭头标记,可以拖动,并且即使移动后,它也会围绕地图上标记的纬度和经度点旋转。
HTML
<body id="document_body" onload="init();">
  <div id="rotation_control">
    Heading&deg;<input id="rotation_value" type="number" size="3" value="0" onchange="setRotation();" />
  </div>
  <div id="map_canvas"></div>
</body>

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;
  }

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;
}

更好的是,这样做可以确保标记是一个Google MVC对象,从而使它拥有MVC对象提供的所有附加方法。

如果你必须使用多彩图像作为标记,则需要创建一个.png精灵表格,并在其中以所需显示的所有角度呈现图像,并基于计算出的两个点之间的方向选择要使用的正确图像。但是,这将不是SVG图像,而是常规标记图像。

希望这些内容有助于您决定如何设置地图标记。


似乎将旋转设置为图标的属性并没有任何作用,无论是作为对图标/选项.icon的更新,还是在标记的构建中。 - Ethan
我认为这是这里唯一正确的答案。简单来说,更新标记图标选项哈希中的旋转,然后使用setOptions方法将标记的图标设置为更新后的哈希即可。就是这样。 - Michael Chaney

4

我能很容易地解决这个问题,只需使用marker.icon.rotation选项指向一个使用svg路径语法的自定义符号就可以了。

$scope.triangle = {
  path: 'M 0 0 L -35 -100 L 35 -100 z',
  fillColor: '#3884ff',
  fillOpacity: 0.7,
  scale: 1,
  strokeColor: '#356cde',
  rotation: 90,
  strokeWeight: 1

}; 

如果使用angular-google-maps库,绑定ui控件来改变triangle.rotation非常容易。
就像我用这个滑块一样。
<slider  ng-model="triangle.rotation" floor="0" ceiling="359" step="5" precsion="1"></slider>

但是你也可以使用论坛。

这是我的Plunker http://plnkr.co/edit/x0egXI


3
这是我实现图像旋转的方式,我考虑了以叠加层形式展现的标记,并将该叠加层定位到位置上,下面的代码将被添加。
没有使用任何额外的库进行旋转,需要解决为叠加层添加点击事件和鼠标事件的问题,这与标记点击事件不同。
通过Google地图标记自定义,地图中会增加内存使用。这也将减少地图中自定义标记的内存消耗。
 <html>
    <head>
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
    <style>html, body {
        height: 100%;
        margin: 0;
        padding: 0;
    }
    #map_canvas {
        height: 100%;
    }
    div.htmlMarker {
        color: red;
        cursor: pointer;
    }

    </style>
    </head>
    <body onload="initialize()">
        <div id="map_canvas"></div>
    </body>

    <script>
    var overlay;

    function initialize() {
        var myLatLng = new google.maps.LatLng(40, -100);
        var mapOptions = {
            zoom: 10,
            center: myLatLng,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };

        var gmap = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);

        function HTMLMarker(lat, lng, rotation) {
            this.lat = lat;
            this.lng = lng;
             this.rotation = rotation;
            this.pos = new google.maps.LatLng(lat, lng);
        }

        HTMLMarker.prototype = new google.maps.OverlayView();
        HTMLMarker.prototype.onRemove = function () {}

        //Initilize your html element here
        HTMLMarker.prototype.onAdd = function () {
            div = document.createElement('DIV');
            div.style.position='absolute';
            div.style.transform='rotate('+this.rotation +'deg)';
            div.style.MozTransform='rotate('+this.rotation +'deg)';
            div.className = "htmlMarker";
           //image source use your own image in src
            div.innerHTML = '<img src="prudvi.png" alt="Mountain View" style="width:25px;height:22px">' ;
            var panes = this.getPanes();
            panes.overlayImage.appendChild(div);
            this.div=div;
        }

        HTMLMarker.prototype.draw = function () {
            var overlayProjection = this.getProjection();
            var position = overlayProjection.fromLatLngToDivPixel(this.pos);
            var panes = this.getPanes();
            this.div.style.left = position.x + 'px';
            this.div.style.top = position.y - 30 + 'px';
        }

        //Added 50 marker with random latlng location and random rotation,
        for (i = 0; i < 50; i++) {
        var PoslatLng = new google.maps.LatLng(myLatLng.lat() + Math.random() - 0.5, myLatLng.lng() + Math.random() - 0.5);
        var htmlMarker = new HTMLMarker(myLatLng.lat() + Math.random() - 0.5,myLatLng.lng() + Math.random() - 0.5, Math.floor(Math.random() * 359));
        htmlMarker.setMap(gmap);
    google.maps.event.addListener(htmlMarker, 'click', function() {
        console.log('clciked')
        gmap.setZoom(8);
        gmap.setCenter(htmlMarker.getPosition());
      });
        }
    }
    </script>
  </html>

但是google.maps.event.addListener不起作用。有什么解决办法吗? - Md. Nashir Uddin

2

最简单的方法可能是使用google.maps.Symbol的旋转属性。只需在创建或更新标记时将其设置为图标的属性即可:

new google.maps.Marker({
  position: map.getCenter(),
  icon: {
    path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
    scale: 7,
    rotation: 193
  },
  map: map
});

Plunker


我不太确定,但是旋转对我好像没有任何作用。我认为它只适用于SVG类型的图标。 - Ethan

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