Fix bounce animation for marker +icon
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

Would like to sponsor an office patch or update to this open source project

https://github.com/scottdejonge/Map-Icons

We need to get both the marker and icon inside the maker to bounce at the same time and speed. Contextually on our implementation we trigger bounce when we hover on a list time which corresponds to the map pin, but we have been unable to get marker and icon to both animate nicely.

Once bounty is done here I'd like to see a commit to the actual project.

Have you had the opportunity to check my solution?
Nuno Freitas over 3 years ago

Crowdsource coding tasks.

1 Solution


My solution consists of a custom bounce animation added to the Marker class that updates its position. Since the MarkerLabel class updates its own position according to the Marker's position, it will also update during the animation.

Bounce function:

google.maps.Marker.prototype.bounce = function (isEnabled) {
    this.isBounceEnabled = isEnabled;

    if (this.isBounceEnabled) {
        // save start position
        this.bounceStartPosition = this.getPosition();

        // bounce by animating between a higher position and the start position
        var self = this,
            duration = 320,
            map = this.getMap(),
            zoom = map.getZoom(),
            diff =  591657550.5 / Math.pow(2, zoom - 1) / (591657550.5 / 10),
            topPosition = new google.maps.LatLng(
                this.bounceStartPosition.lat() + diff,
                this.bounceStartPosition.lng()),
            bounceAnimation = function () {
                self.animateTo(topPosition, duration, function () {
                    self.animateTo(self.bounceStartPosition, duration, bounceAnimation);
                });
            };
        bounceAnimation();

        // handle zoom changes
        google.maps.event.removeListener(this.bounceZoomListener);
        this.bounceZoomListener = google.maps.event.addListener(map, 'zoom_changed', function () {
            if (self.isBounceEnabled) {
                // reset bounce on zoom change
                self.setPosition(self.bounceStartPosition);
                self.bounce(true);
            }
        });
    } else {
        // return to original position when stopping bounce
        if (this.bounceStartPosition !== undefined) {
            this.animateTo(this.bounceStartPosition, 160);
        }

        // remove zoom change listener
        google.maps.event.removeListener(this.bounceZoomListener);
    }
};

You can call it on any marker, passing true or false to enable or disable it:

marker.bounce(true);

And the function that handles the animation:

google.maps.Marker.prototype.animateTo = function (newPosition, duration, completeCallback) {
    var self = this,
        startLat = this.getPosition().lat(),
        startLng = this.getPosition().lng(),
        endLat = newPosition.lat(),
        endLng = newPosition.lng(),
        animateStep = function (startDate) {
            var ellapsedTime = new Date() - startDate,
                durationRatio = ellapsedTime / duration,
                easingDurationRatio = 0.5 - Math.cos(durationRatio * Math.PI) / 2;

            if (durationRatio < 1) {
                var deltaLat = startLat + (endLat - startLat) * easingDurationRatio,
                    deltaLng = startLng + (endLng - startLng) * easingDurationRatio,
                    deltaPosition = new google.maps.LatLng(deltaLat, deltaLng);
                self.setPosition(deltaPosition);
                self.animateHandler = window.requestAnimationFrame(function () {
                    animateStep(startDate);
                });
            } else {
                self.setPosition(newPosition);

                if (typeof completeCallback === 'function') {
                    completeCallback();
                }
            }
        };
    window.cancelAnimationFrame(this.animateHandler);
    animateStep(new Date());
};

In the demo I also included a polyfill for requestAnimationFrame.

Let me know if this suits your needs and if you have any questions.