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

32

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

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

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

17个回答

2

首先,想法是在隐藏的画布上绘制旋转的标记图像。

假设您有一个隐藏的画布:

<canvas id="carCanvas" width="50" height="50" style="display:none"></canvas>

现在,您可以这样做:

function updateCarMarker(i,lat, lng, icon = "img/carIcon.png") {
    var latLong = new google.maps.LatLng(lat, lng);
    if (!carMarkers[i]){
        var carImage = new Image();
        carImage.onload = ()=>{
            drawMovedCar(i,latLong,carImage);
        }
        carImage.src=icon;
    } else {
        drawMovedCar(i,latLong,carMarkers[i].carImage);
    }
}

function drawMovedCar(i,latLong,I){
    let m=carMarkers[i];
    let canvas = document.getElementById("carCanvas");
    let C = canvas.getContext('2d');
    if (m){
        var distance = google.maps.geometry.spherical.computeDistanceBetween(
                                                               m.getPosition(), latLong);
        var deg = (distance<2)?carMarkers[i].deg
                              :google.maps.geometry.spherical.computeHeading(m, latLong);
        carMarkers[i].setMap(null);
     } else {
        var deg=0;
     }
    C.save();
        C.clearRect(0, 0, canvas.width, canvas.height);
        C.translate(canvas.width/2,canvas.height/2);
        C.rotate(deg * Math.PI / 180);
        C.scale(0.4,0.4);
        C.drawImage(I,-I.width/2,-I.height/2,I.width,I.height);           
    C.restore();
    if (!m){
        m = new google.maps.Marker({
            position: latLong,
            map: map,
            icon: canvas.toDataURL("image/png",1)
        });
        m.deg = deg;
        m.carImage = I;
        carMarkers[i]=m;  
    } else {
        m.setIcon(canvas.toDataURL("image/png",1));
        m.setPosition(latLong);
    }
}

上面是我的原始代码。我保留了它的完整性,以便您可以看到我的其他优化。

惊人的解决方案! - User-8017771
嗨,如何在React JS中使用这个? - Amir Farahani

2
每次图像更改时,您可以调用yourmarker.setIcon(canvas.toDataUrlOrSomeThig)。我在API参考中没有看到直接使用canvas元素的内容,除非您自己实现google.maps.OverlayView。
如果您只想要动画效果,可以使用gif,并将标记选项optimized:false添加到其中。

我之前不知道这个 toDataUrl 函数,但在这里找到了它 (https://dev59.com/GXNA5IYBdhLWcg3wgeId) - 我会尝试看看标记是否真的能够识别它。- 这是个好主意。 - Horst Walter
使用canvas.toDataURL("image/png");不要像你的链接中那样删除'date:...'部分。 - Gerben
至少在IE9中,我会得到“对象不支持方法或属性”。 代码:var canv = document.getElementById(“c”); var u = canv.toDataURL(“image / png”); 并且在HTML中:<canvas id =“c”width =“150”height =“150”> <img alt =“Plane”width =“32”height =“32”src =“Images / Aircraft.png”/> </ canvas> - Horst Walter
显然这不是我的问题,参见https://dev59.com/P0bRa4cB1Zd3GeqP4Nea。不幸的是,这使得这种(好的)方法对我的情况无效。无论如何,谢谢你的提示。 - Horst Walter

1
使用MarkerWithLabel库,您可以以以下方式实现:
var ico = document.createElement('img');
ico.src = 'ImageSource';
ico.setAttribute('style', 'transform:rotate('30deg);');
mapMarkers[0].labelContent = ico;
mapMarkers[0].label.draw();

1
如果你正在使用SVG,则这是旋转它的最佳方法。

let marker_, svg_, size_ = 100, rotation_ = 50


// Get SVG
fetch('https://upload.wikimedia.org/wikipedia/commons/7/78/Space-shuttle.svg')
.then(response => response.text())
.then(text => {
  svg_ = text;
  svg_ = svg_
    .replace(/^<\?(.+)\?>$/gm, '') // unsupported unnecessary line
    // You can replace anything you want, but first of all check your svg code
    .replace(/width.+\Wheight\S+/, 
      'width="{{width}}" height="{{height}}" transform="{{transform}}" ')
    
  // Load Map
  initMap()
})

function getIcon(rotation){
return {url:`data:image/svg+xml;charset=utf-8,
      ${encodeURIComponent(svg_
      .replace('{{width}}', 100)
      .replace('{{height}}', 100)
      .replace('{{transform}}', `rotate(${rotation},0,0)`))}`,anchor: new google.maps.Point(50, 50),
      origin: new google.maps.Point(0, 0)}

}


// Map
function initMap() {
  const position = {lat: 36.720426, lng: -4.412573};
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 19,
    center: position
  })
  marker_ = new google.maps.Marker({
    position: position,
    map: map,
    icon: getIcon(rotation_)
  })
}

// Change rotation
$input_ = document.querySelector('input')
$input_.value = rotation_
$input_.onchange = () => {
  marker_.setIcon(getIcon(parseInt($input_.value))
  )
} 
* {
  padding: 0;
  margin: 0;
}

#map {
  width: 100%;
  height: 100vh;
}

input {
  position: fixed;
  z-index: 1;
  margin: 100px;
  padding: 10px;
  border-radius: 2px;
  background-color: red;
  border: none;
  color: white;
  font-family: 'Roboto';
  width: 70px;
}
<script src="https://maps.google.com/maps/api/js"></script>
<input type="number" placeholder="rotation">
<div id="map"></div>


1
假设你只在谷歌地图中使用该图片,你可以执行以下操作。
bearing = 20

document.querySelectorAll('img[src="/images/imageName"]').forEach((node) => {

    node.style['transform'] = `rotate(${bearing}deg)`
    node.style['webkitTransform'] = `rotate(${bearing}deg)`
    node.style['MozTransform'] = `rotate(${bearing}deg)`
    node.style['msTransform'] = `rotate(${bearing}deg)`
    node.style['OTransform'] = `rotate(${bearing}deg)`
})

这会深入到dom树中,设置标记图标的转换,以旋转所需的度数。图像imageName应该面向北方。
不确定是否需要webkit、Moz、ms和O版本,但是嘿 ‍♂️也无妨。

0

我找到了一种简单的方法来旋转Google标记的PNG图像标记。通过创建一个自定义标记覆盖google.maps.OverlayView并使用CSS/内联样式轻松旋转图像。

export const createCustomMarker = ({ OverlayView = google.maps.OverlayView, ...args }) => {

class GoogleMarker extends OverlayView {

    options: any = {};
    div: any = null;
    innerHtml: any = null;

    constructor(options) {
        super();
        this.options = options;
        this.setMap(options.map);
    }

    createDiv() {
        const options = this.options;
        this.div = document.createElement('div');
        this.div.style.position = 'absolute';
        this.setRotation(this.options.rotation);
        if (options.icon) {
            this.setInnerHtml(this.getInnerImageHtml(options));
        }
    }

    getInnerImageHtml(options) {
        const size = this.getSize(options);
        const label = this.options.label;
        const labelHtml = label ? `<span style="color:black;margin-left: -40px;width: 100px;text-align: center;display: block;font-weight:bold;">${label}</span>` : "";
        return `<img style="height:${size.height}px;width:${size.width}px" id="${options.id || ''}" src="${options.icon}">${labelHtml}`;
    }

    addListeners() {
        const self = this;
        google.maps.event.addDomListener(this.div, 'click', event => {
            google.maps.event.trigger(self, 'click');
        });

        this.div.onmouseenter = function () {
            debugger
            google.maps.event.trigger(self, 'onmouseenter');
        }

        this.div.onmouseover = function () {
            google.maps.event.trigger(self, 'onmouseover');
        }

        this.div.onmouseleave = function () {
            google.maps.event.trigger(self, 'onmouseleave');
        }

        this.div.onmouseout = function () {
            google.maps.event.trigger(self, 'onmouseout');
        }
    }

    appendDivToOverlay(appendDiv: any) {
        const panes: google.maps.MapPanes = this.getPanes();
        panes.floatPane.appendChild(appendDiv);
    }

    setRotation(degrees: number) {
        if (this.div) {
            this.div.style.transform = 'rotate(' + degrees + 'deg)';
        }
        this.options.rotation = degrees;
    }

    getRotation() {
        return this.options.rotation;
    }

    setInnerHtml(html: string) {
        this.innerHtml = html;
        this.div.innerHTML = this.innerHtml;
    }

    private positionDiv(div: any, options: any) {
        if (div != null) {
            const point = this.getProjection().fromLatLngToDivPixel(options.latlng);
            if (point) {
                const size = this.getSize(options);
                const anchor = options.anchor ? options.anchor : new google.maps.Point((size.width / 2), (size.height / 2))
                const leftAnchor = anchor.x;
                const topAnchor = anchor.y;
                div.style.left = `${point.x - leftAnchor}px`;
                div.style.top = `${point.y - topAnchor}px`;
            }
        }
    }

    private getSize(options) {
        const size = options.size || { height: 52, width: 52 };
        return size;
    }

    draw() {
        if (!this.div) {
            this.createDiv();
            this.appendDivToOverlay(this.div);
            this.addListeners();
        }
        this.positionDiv(this.div, this.options);
    }

    remove() {
        if (this.div) {
            this.div.parentNode.removeChild(this.div);
            this.div = null;
        }
    }

    setVisible(value: boolean) {
        if (this.div) {
            this.div.style["display"] = value ? "block" : "none";
        }
    }

    getVisible() {
        if (this.div) {
            return this.div.style["display"] == "none";
        }
        return false;
    }

    setPosition(position) {
        this.options.latlng = position;
        this.infoOptions.latlng = position;
        this.positionDiv(this.div, this.options);
    }

    getPosition() {
        return this.options.latlng;
    }

    getDraggable() {
        return false;
    }

    isHTML(html: string) {
        return /<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)<\/\1>/.test(html);
    }
}
return new GoogleMarker(args)

}

创建此自定义标记后,按以下方式初始化标记。
import { createCarMarker } from "./marker.component"; // dynamic path to component

let marker = createCarMarker({
        id: id, // will add id to the parent container div
        latlng: new google.maps.LatLng(0, 0), // replace latitude-longitude with your values
        map: this.map,
        size: new google.maps.Size(52, 52), // replace the image size with your values
        rotation: markerData.direction, // Provide values in degrees
        icon: iconUrl, // Replace it with your image url
        label: markerLabel // Provide marker label. Optional field
    });

现在只需使用以下方法旋转标记

marker.setRotation(180); // You just need to call only this method every-time the degrees changes.

监听标记的变化。

google.maps.event.addDomListener(marker, 'click', function (event) {

    });

    google.maps.event.addListener(marker, 'onmouseenter', function (event) {

    });

    google.maps.event.addListener(marker, 'onmouseleave', function (event) {

    });

    google.maps.event.addListener(marker, 'onmouseover', function (event) {

    });

    google.maps.event.addListener(marker, 'onmouseout', function (event) {

    });

您可以根据需要自定义监听器或在自定义标记类中添加新的/更新。


-1
var icon = {
   path: aeroplanePath/image,
   fillColor: '#0000FF',
   fillOpacity: .6,
   anchor: new google.maps.Point(0,0),
   strokeWeight: 0,
   scale: 1,
   rotation: 180
}


var marker = new google.maps.Marker({
   position: positions[k],
   icon: icon,
   draggable: true,
   title: "BOING-707",    
});

1
图标不支持旋转:https://developers.google.com/maps/documentation/javascript/3.exp/reference#Icon - PanMan

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