Rotating image / marker image on Google map V3

2019-01-03 10:11发布

How could I rotate an image (marker image) on a Google map V3?

  • There is an excellent example for V2 here, exactly doing what I need. But for GMap2! They do it with a rotating canvas.
  • Image rotating with JS / JQuery is frequently used, there are multiple answers about this. But how could I apply this to my maps image?
  • One mentioned approach is to have different images for different angles and to switch among them - this is NOT what I want. I do not like to have so many images, I want to rotate by code.

Remark: There are similar questions, but all for V2 and not V3 (as far I can tell). I need it for V3.

14条回答
何必那么认真
2楼-- · 2019-01-03 10:38

I have done the rotation in v3 with the following code:

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

and in the animation method :

if (supportsCanvas) {
                    angle = bearing(new google.maps.LatLng(lat1, lng1),new google.maps.LatLng(lat2, lng2));
                    plotcar();
                }

I hope that help.

查看更多
干净又极端
3楼-- · 2019-01-03 10:40

I have found two extensions to the Google MAP V3: infobox.js and markerwithlabel.js Both can handle an image DOM element as content, which in turn I can rotate via the jQuery image rotate plugin.

This even works without setting the marker's image again after rotation.

Edit: As of questions / comments below:

The extension for label is required, because it can handle other DOM elements. So I can add arbitrary HTML as label, in my particular case I add the image. And then I do rotate this image (child of the label) with the rotate plugin. So assign the image an id in order to easily access it. Actually I am using one label just for the image, and another for descriptive text.

Edit 2: Due to Stephan's comment on the DOM readiness

In my code I have found the following lines. This shows that I force a draw on the label before rotating the image.

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

Edit 3: Code example how to create the 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);
查看更多
Emotional °昔
4楼-- · 2019-01-03 10:40

Nobody mentioned about using pre-rotated icons. Depending on your application, you could take one icon and rotate it +10 degrees, +20 degrees ... +350 degrees and instead of rotating marker itself, just assign different icon to it - one out of 36 if 10 degrees resolution is good enough. That's also very light on client's resources.

In the example below I generated 36 icons, every one of them is 10 degrees rotated. Their names are: icon0.png, icon10.png, icon20.png, ... icon340.png, icon350.png, icon360.png. The 0 and 360 are the very same icon (e.g symlink)

var rotation = 123 // degrees
var iconName = "icon" + (Math.round(rotation/10)*10).toString() + ".png"
var marker = new google.maps.Marker({
    icon: iconName
})
查看更多
Explosion°爆炸
5楼-- · 2019-01-03 10:44

I also had a hard time to figure out the way to rotate .png marker. I solved it like below. You can create many markers with same custom image and rotate a specific marker you want to rotate. I hope it helpful to you.

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)'}); 
查看更多
三岁会撩人
6楼-- · 2019-01-03 10:44

You could call the yourmarker.setIcon(canvas.toDataUrlOrSomeThig) every time the image changes. I don't see anything in the api reference for using the canvas element directly, except if you implement you own google.maps.OverlayView.

If you only want animation you could use a gif, and add the marker option optimized: false to it.

查看更多
萌系小妹纸
7楼-- · 2019-01-03 10:45

The idea is to first draw the rotated marker image on a hidden canvas.

Say, you have a hidden canvas:

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

Now you can do this:

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

The above is my original code. I have left it intact so that you can see my other optimizations.

查看更多
登录 后发表回答