A css map marker with animations on a google map.
Dependencies / Credits
- jQuery
- Google Maps
- dynamics.js | Michaël Villar
- Map marker CSS only | Andreas Storm
A Pen by Jerome Truong on CodePen.
A css map marker with animations on a google map.
Dependencies / Credits
A Pen by Jerome Truong on CodePen.
<link href='//fonts.googleapis.com/css?family=Raleway:600,900,400|Roboto:300,700' rel='stylesheet'> | |
<div id="map"></div> | |
<div class="header"> | |
<h1>Map Marker</h1> | |
<ul class="animations"> | |
<li><a href="#" id="drop">Drop</a> | |
<li><a href="#" id="wobble">Wobble</a> | |
<li><a href="#" id="bounce">Bounce</a> | |
</ul> | |
<div class='list-label'>--- animations ---</div> | |
<div class='tags'>CSS | Google Maps | <a href="http://dynamicsjs.com/">dynamics.js</a></div> | |
</div> |
CustomMarker.prototype = new google.maps.OverlayView(); | |
function CustomMarker(opts) { | |
this.setValues(opts); | |
} | |
CustomMarker.prototype.draw = function() { | |
var self = this; | |
var div = this.div; | |
if (!div) { | |
div = this.div = $('' + | |
'<div>' + | |
'<div class="shadow"></div>' + | |
'<div class="pulse"></div>' + | |
'<div class="pin-wrap">' + | |
'<div class="pin"></div>' + | |
'</div>' + | |
'</div>' + | |
'')[0]; | |
this.pinWrap = this.div.getElementsByClassName('pin-wrap'); | |
this.pin = this.div.getElementsByClassName('pin'); | |
this.pinShadow = this.div.getElementsByClassName('shadow'); | |
div.style.position = 'absolute'; | |
div.style.cursor = 'pointer'; | |
var panes = this.getPanes(); | |
panes.overlayImage.appendChild(div); | |
google.maps.event.addDomListener(div, "click", function(event) { | |
google.maps.event.trigger(self, "click", event); | |
}); | |
} | |
var point = this.getProjection().fromLatLngToDivPixel(this.position); | |
if (point) { | |
div.style.left = point.x + 'px'; | |
div.style.top = point.y + 'px'; | |
} | |
}; | |
CustomMarker.prototype.animateDrop = function() { | |
dynamics.stop(this.pinWrap); | |
dynamics.css(this.pinWrap, { | |
'transform': 'scaleY(2) translateY(-'+$('#map').outerHeight()+'px)', | |
'opacity': '1', | |
}); | |
dynamics.animate(this.pinWrap, { | |
translateY: 0, | |
scaleY: 1.0, | |
}, { | |
type: dynamics.gravity, | |
duration: 1800, | |
}); | |
dynamics.stop(this.pin); | |
dynamics.css(this.pin, { | |
'transform': 'none', | |
}); | |
dynamics.animate(this.pin, { | |
scaleY: 0.8 | |
}, { | |
type: dynamics.bounce, | |
duration: 1800, | |
bounciness: 600, | |
}) | |
dynamics.stop(this.pinShadow); | |
dynamics.css(this.pinShadow, { | |
'transform': 'scale(0,0)', | |
}); | |
dynamics.animate(this.pinShadow, { | |
scale: 1, | |
}, { | |
type: dynamics.gravity, | |
duration: 1800, | |
}); | |
} | |
CustomMarker.prototype.animateBounce = function() { | |
dynamics.stop(this.pinWrap); | |
dynamics.css(this.pinWrap, { | |
'transform': 'none', | |
}); | |
dynamics.animate(this.pinWrap, { | |
translateY: -30 | |
}, { | |
type: dynamics.forceWithGravity, | |
bounciness: 0, | |
duration: 500, | |
delay: 150, | |
}); | |
dynamics.stop(this.pin); | |
dynamics.css(this.pin, { | |
'transform': 'none', | |
}); | |
dynamics.animate(this.pin, { | |
scaleY: 0.8 | |
}, { | |
type: dynamics.bounce, | |
duration: 800, | |
bounciness: 0, | |
}); | |
dynamics.animate(this.pin, { | |
scaleY: 0.8 | |
}, { | |
type: dynamics.bounce, | |
duration: 800, | |
bounciness: 600, | |
delay: 650, | |
}); | |
dynamics.stop(this.pinShadow); | |
dynamics.css(this.pinShadow, { | |
'transform': 'none', | |
}); | |
dynamics.animate(this.pinShadow, { | |
scale: 0.6, | |
}, { | |
type: dynamics.forceWithGravity, | |
bounciness: 0, | |
duration: 500, | |
delay: 150, | |
}); | |
} | |
CustomMarker.prototype.animateWobble = function() { | |
dynamics.stop(this.pinWrap); | |
dynamics.css(this.pinWrap, { | |
'transform': 'none', | |
}); | |
dynamics.animate(this.pinWrap, { | |
rotateZ: -45, | |
}, { | |
type: dynamics.bounce, | |
duration: 1800, | |
}); | |
dynamics.stop(this.pin); | |
dynamics.css(this.pin, { | |
'transform': 'none', | |
}); | |
dynamics.animate(this.pin, { | |
scaleX: 0.8 | |
}, { | |
type: dynamics.bounce, | |
duration: 800, | |
bounciness: 1800, | |
}); | |
} | |
$(function() { | |
var pos = new google.maps.LatLng(42.9837, -81.2497); | |
var map = new google.maps.Map(document.getElementById('map'), { | |
zoom: 14, | |
center: pos, | |
disableDefaultUI: true, | |
}); | |
var marker = new CustomMarker({ | |
position: pos, | |
map: map, | |
}); | |
google.maps.event.addListener(marker, 'click', function(e) { | |
marker.animateWobble(); | |
}); | |
$('#drop').on('click', function(e) { | |
marker.animateDrop(); | |
}); | |
$('#wobble').on('click', function(e) { | |
marker.animateWobble(); | |
}); | |
$('#bounce').on('click', function(e) { | |
marker.animateBounce(); | |
}) | |
}); |
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
<script src="//maps.google.com/maps/api/js"></script> | |
<script src="//github.com/michaelvillar/dynamics.js/releases/download/0.0.8/dynamics.min.js"></script> |
html, | |
body, | |
#map { | |
width: 100%; | |
height: 100%; | |
margin: 0; | |
padding: 0; | |
border: 0; | |
} | |
// Thanks to: https://www.antimath.info/css/sass-sqrt-function/ | |
// Using sqrt to get the size of the map pin. You could do this manually, | |
// if your pin is a different shape. | |
@function sqrt($r) { | |
$x0: 1; | |
$x1: $x0; | |
@for $i from 1 through 10 { | |
$x1: $x0 - ($x0 * $x0 - abs($r)) / (2 * $x0); | |
$x0: $x1; | |
} | |
@return $x1; | |
} | |
$pinWidth: 100px; | |
$pinHeightFactor: ((1 + sqrt(2))/2); | |
$pinHeight: $pinHeightFactor * $pinWidth; | |
$pinColor: #f93c11; | |
$shadowOpacity: .5; | |
$shadow-size: 50px; | |
$pulseSize: 100px; | |
.pin-wrap { | |
position: absolute; | |
width: $pinWidth; | |
height: $pinWidth; | |
margin-top: -$pinHeight; | |
margin-left: -$pinWidth/2; | |
transform-origin: 50% ($pinHeightFactor * 100%) 0; | |
} | |
.pin { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
width: $pinWidth; | |
height: $pinWidth; | |
margin-top: -$pinWidth/2; | |
margin-left: -$pinWidth/2; | |
transform-origin: 50% ($pinHeightFactor * 100%) 0; | |
} | |
.pin::after { | |
position: absolute; | |
display: block; | |
box-sizing: border-box; | |
width: $pinWidth; | |
height: $pinWidth; | |
content: ''; | |
transform: rotateZ(-45deg); | |
border: 20px solid $pinColor; | |
border-radius: 50% 50% 50% 50%; | |
} | |
.pin::before { | |
position: absolute; | |
display: block; | |
box-sizing: border-box; | |
width: $pinWidth; | |
height: $pinWidth; | |
content: ''; | |
transform: rotateZ(-45deg); | |
border: 18px solid darken($pinColor, 10%); | |
border-radius: 50% 50% 50% 0; | |
; | |
} | |
.shadow { | |
position: absolute; | |
} | |
.shadow::after { | |
position: absolute; | |
left: -100px - $shadow-size/2; | |
display: block; | |
width: $shadow-size; | |
height: $shadow-size; | |
margin-top: -$shadow-size/2; | |
content: ''; | |
transform: rotateX(55deg); | |
border-radius: 50%; | |
box-shadow: rgba(0, 0, 0, $shadowOpacity) 100px 0 20px; | |
} | |
.pulse { | |
position: absolute; | |
margin-top: -$pulseSize/2; | |
margin-left: -$pulseSize/2; | |
transform: rotateX(55deg); | |
} | |
.pulse::after { | |
display: block; | |
width: $pulseSize; | |
height: $pulseSize; | |
content: ''; | |
animation: pulsate 1s ease-out; | |
animation-delay: 1.1s; | |
animation-iteration-count: infinite; | |
opacity: 0; | |
border-radius: 50%; | |
box-shadow: 0 0 1px 2px rgba(0, 0, 0, $shadowOpacity); | |
box-shadow: 0 0 6px 3px rgba($pinColor, 1.0); | |
} | |
@keyframes pulsate { | |
0% { | |
transform: scale(.1, .1); | |
opacity: 0; | |
} | |
50% { | |
opacity: 1; | |
} | |
100% { | |
transform: scale(1.2, 1.2); | |
opacity: 0; | |
} | |
} | |
.header { | |
font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
font-size: 16px; | |
line-height: 2em; | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
width: 100%; | |
padding: 10px 0; | |
text-align: center; | |
color: $pinColor; | |
background: rgba(white, .75); | |
} | |
h1 { | |
font-family: Raleway, 'Century Gothic', CenturyGothic, AppleGothic, sans-serif; | |
font-weight: 900; | |
font-size: 32px; | |
margin-bottom: 10px; | |
border-bottom: 2px dashed $pinColor; | |
display: inline-block; | |
} | |
.tags { | |
font-weight: 300; | |
} | |
.list-label { | |
font-family: Raleway, 'Century Gothic', CenturyGothic, AppleGothic, sans-serif; | |
font-weight: 400; | |
} | |
.header li { | |
display: inline-block; | |
} | |
.tags a{ | |
color: $pinColor; | |
} | |
$buttonHeight: 44px; | |
.animations a { | |
line-height: $buttonHeight - 2px; | |
display: block; | |
box-sizing: border-box; | |
height: $buttonHeight; | |
margin: 0 5px; | |
padding: 0 10px; | |
text-decoration: none; | |
color: $pinColor; | |
border: 2px solid $pinColor; | |
border-radius: ($buttonHeight / 2); | |
transition: background-color 0.3s, color 0.3s; | |
} | |
.animations a:hover { | |
color: white; | |
background: $pinColor; | |
} |