javascript CSS 谷歌地图自定义信息窗口
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5891875/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
CSS Google Maps Custom InfoWindow
提问by Andrew
I've been using code from http://gmaps-samples-v3.googlecode.com/svn/trunk/infowindow_custom/infowindow-custom.html, which is currently google's best example of how to create custom InfoWindow's in Maps API v3. I've been working on it and so far I've got it close to working except for one thing, it the div container the text content won't expand to fit the content, so it just drops off instead of expanding the bubble. if I give the content container a fixed pixel width it works fine but I can't get it to expand depending on the amount of text in it.
我一直在使用来自http://gmaps-samples-v3.googlecode.com/svn/trunk/infowindow_custom/infowindow-custom.html 的代码,这是目前谷歌关于如何在 Maps API v3 中创建自定义 InfoWindow 的最佳示例。我一直在研究它,到目前为止我已经接近工作了,除了一件事,文本内容不会扩展到适合内容的 div 容器,所以它只是下降而不是扩展气泡。如果我给内容容器一个固定的像素宽度,它可以正常工作,但我无法根据其中的文本量扩展它。
I've been stuck on this one for a while. Any help would be greatly appreciated!
我已经被这个问题困住了一段时间。任何帮助将不胜感激!
Here is the HTML page
这是 HTML 页面
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Gayborhood Map Test</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0px; padding: 0px }
#map_canvas { width: 900px;
height: 400px;
margin: 200px auto 0 auto; }
</style>
<link rel="stylesheet" type="text/css" href="map.css" />
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script type="text/javascript" src="InfoBox.js"></script>
<script type="text/javascript">
function initialize() {
var latlng = new google.maps.LatLng(39.947137,-75.161824);
var myOptions = {
zoom: 16,
center: latlng,
mapTypeId: google.maps.MapTypeId.HYBRID
};
var gayborhood;
var map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
var gayborhoodcoords = [
new google.maps.LatLng(39.9492017, -75.1631272),
new google.maps.LatLng(39.945423, -75.1639561),
new google.maps.LatLng(39.9450064, -75.160579),
new google.maps.LatLng(39.9487765, -75.1597468),
new google.maps.LatLng(39.9492017, -75.1631272)
];
gayborhood = new google.maps.Polygon({
paths: gayborhoodcoords,
strokeColor: "#00ff00",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#00ff00",
fillOpacity: 0.35
});
gayborhood.setMap(map);
var image = 'red_icon.png';
var myLatLng = new google.maps.LatLng(39.948883,-75.162246);
var redMarker = new google.maps.Marker({
position: myLatLng,
map: map,
icon: image
});
var contentString = '<h4>Woody\'s Bar</h4>';
/*var infowindow = new google.maps.InfoWindow({
content: contentString,
disableAutoPan: true
});*/
google.maps.event.addListener(redMarker, 'mouseover', function() {
var infoBox = new InfoBox({marker: redMarker, map: map});
});
/*google.maps.event.addListener(redMarker, 'mouseout', function() {
infowindow.close();
});*/
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas"></div>
</body>
</html>
Here's the InfoBox.js:
这是 InfoBox.js:
/* An InfoBox is like an info window, but it displays
* under the marker, opens quicker, and has flexible styling.
* @param {GLatLng} latlng Point to place bar at
* @param {Map} map The map on which to display this InfoBox.
* @param {Object} opts Passes configuration options - content,
* offsetVertical, offsetHorizontal, className, height, width
*/
function InfoBox(opts) {
google.maps.OverlayView.call(this);
this.marker_ = opts.marker
this.latlng_ = opts.marker.getPosition();
this.map_ = opts.map;
this.offsetVertical_ = -65;
this.offsetHorizontal_ = -20;
this.height_ = 50;
//this.width_ = 159;
var me = this;
this.boundsChangedListener_ =
google.maps.event.addListener(this.map_, "bounds_changed", function() {
return me.panMap.apply(me);
});
// Once the properties of this OverlayView are initialized, set its map so
// that we can display it. This will trigger calls to panes_changed and
// draw.
this.setMap(this.map_);
}
/* InfoBox extends GOverlay class from the Google Maps API
*/
InfoBox.prototype = new google.maps.OverlayView();
/* Creates the DIV representing this InfoBox
*/
InfoBox.prototype.remove = function() {
if (this.div_) {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
};
/* Redraw the Bar based on the current projection and zoom level
*/
InfoBox.prototype.draw = function() {
// Creates the element if it doesn't exist already.
this.createElement();
if (!this.div_) return;
// Calculate the DIV coordinates of two opposite corners of our bounds to
// get the size and position of our Bar
var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_);
if (!pixPosition) return;
// Now position our DIV based on the DIV coordinates of our bounds
//this.div_.style.width = this.width_ + "px";
this.div_.style.left = (pixPosition.x + this.offsetHorizontal_) + "px";
this.div_.style.height = this.height_ + "px";
this.div_.style.top = (pixPosition.y + this.offsetVertical_) + "px";
this.div_.style.display = 'block';
};
/* Creates the DIV representing this InfoBox in the floatPane. If the panes
* object, retrieved by calling getPanes, is null, remove the element from the
* DOM. If the div exists, but its parent is not the floatPane, move the div
* to the new pane.
* Called from within draw. Alternatively, this can be called specifically on
* a panes_changed event.
*/
InfoBox.prototype.createElement = function() {
var panes = this.getPanes();
var div = this.div_;
if (!div) {
// This does not handle changing panes. You can set the map to be null and
// then reset the map to move the div.
div = this.div_ = document.createElement("div");
div.className = "infobox";
//div.style.width = this.width_ + "px";
//div.style.height = this.height_ + "px";
var leftDiv = document.createElement("div");
leftDiv.className = "bubbleLeftDiv";
var containerDiv = document.createElement("div");
containerDiv.className = "infoboxContainer";
var contentDiv = document.createElement("div");
contentDiv.className = "infoboxContent";
var title = "Much longer title than woody's"
//var infoboxWidth = ( title.length*10 - (title.length) - 40) + "px"
//containerDiv.style.width = infoboxWidth;
//this.width_ = infoboxWidth + 47;
contentDiv.innerHTML = "<h3>" + title + "</h3>";
var rightDiv = document.createElement("div");
rightDiv.className = "bubbleRightDiv";
function removeInfoBox(ib) {
return function() {
ib.setMap(null);
};
}
google.maps.event.addListener(this.marker_, 'mouseout', removeInfoBox(this));
div.appendChild(leftDiv)
div.appendChild(containerDiv);
containerDiv.appendChild(contentDiv);
div.appendChild(rightDiv);
div.style.display = 'none';
panes.floatPane.appendChild(div);
this.panMap();
} else if (div.parentNode != panes.floatPane) {
// The panes have changed. Move the div.
div.parentNode.removeChild(div);
panes.floatPane.appendChild(div);
} else {
// The panes have not changed, so no need to create or move the div.
}
}
/* Pan the map to fit the InfoBox.
*/
InfoBox.prototype.panMap = function() {
// if we go beyond map, pan map
var map = this.map_;
var bounds = map.getBounds();
if (!bounds) return;
// The position of the infowindow
var position = this.latlng_;
// The dimension of the infowindow
var iwWidth = this.width_;
var iwHeight = this.height_;
// The offset position of the infowindow
var iwOffsetX = this.offsetHorizontal_;
var iwOffsetY = this.offsetVertical_;
// Padding on the infowindow
var padX = 40;
var padY = 40;
// The degrees per pixel
var mapDiv = map.getDiv();
var mapWidth = mapDiv.offsetWidth;
var mapHeight = mapDiv.offsetHeight;
var boundsSpan = bounds.toSpan();
var longSpan = boundsSpan.lng();
var latSpan = boundsSpan.lat();
var degPixelX = longSpan / mapWidth;
var degPixelY = latSpan / mapHeight;
// The bounds of the map
var mapWestLng = bounds.getSouthWest().lng();
var mapEastLng = bounds.getNorthEast().lng();
var mapNorthLat = bounds.getNorthEast().lat();
var mapSouthLat = bounds.getSouthWest().lat();
// The bounds of the infowindow
var iwWestLng = position.lng() + (iwOffsetX - padX) * degPixelX;
var iwEastLng = position.lng() + (iwOffsetX + iwWidth + padX) * degPixelX;
var iwNorthLat = position.lat() - (iwOffsetY - padY) * degPixelY;
var iwSouthLat = position.lat() - (iwOffsetY + iwHeight + padY) * degPixelY;
// calculate center shift
var shiftLng =
(iwWestLng < mapWestLng ? mapWestLng - iwWestLng : 0) +
(iwEastLng > mapEastLng ? mapEastLng - iwEastLng : 0);
var shiftLat =
(iwNorthLat > mapNorthLat ? mapNorthLat - iwNorthLat : 0) +
(iwSouthLat < mapSouthLat ? mapSouthLat - iwSouthLat : 0);
// The center of the map
var center = map.getCenter();
// The new map center
var centerX = center.lng() - shiftLng;
var centerY = center.lat() - shiftLat;
// center the map to the new shifted center
map.setCenter(new google.maps.LatLng(centerY, centerX));
// Remove the listener after panning is complete.
google.maps.event.removeListener(this.boundsChangedListener_);
this.boundsChangedListener_ = null;
};
And here's the CSS:
这是 CSS:
.infobox {
border: 0px none;
position: absolute;
width: auto;
height: auto;
}
.infoboxContent {
font-family: arial, helvetica, sans-serif;
font-size: 15px;
padding: 0px;
margin: 9px 0px 0px -24px;
position: absolute;
z-index: 105;
}
.infoboxContainer {
background: url('infowindow_bg.png') repeat-x;
height: 50px;
margin-left: 47px;
}
.bubbleLeftDiv {
width: 47px;
height: 50px;
background: url('infowindow_left.png') no-repeat;
position: absolute;
z-index: 102;
}
.bubbleRightDiv {
width: 26px;
height: 50px;
background: url('infowindow_right.png') no-repeat;
position: absolute;
right: -26px;
top: 0px;
}
.clear { clear: both; }
Thank you!!
谢谢!!
回答by planewalker
I've faced the same problem. The approach that worked for me was to dynamically determine the dimensions of the content and set the height and width of the InfoBox correctly. The problem that I encountered with this was that before the content is inserted into the DOM it doesn't have (correct) dimension values. As a result my approach was the following:
我遇到了同样的问题。对我有用的方法是动态确定内容的尺寸并正确设置 InfoBox 的高度和宽度。我遇到的问题是,在将内容插入 DOM 之前,它没有(正确的)维度值。因此,我的方法如下:
- Create the DOM content element that needs to be inserted
- Insert it in a temp container
- Get dimensions of temp container
- Remove temp container
- Insert content in InfoBox and set its height and width based on temp container dimensions
- 创建需要插入的DOM内容元素
- 将其插入临时容器中
- 获取临时容器的尺寸
- 移除临时容器
- 在 InfoBox 中插入内容并根据临时容器尺寸设置其高度和宽度
Here is an example done with the jQuery framework:
这是使用 jQuery 框架完成的示例:
var temp = $("<div class='temp'></div>").html(content).hide().appendTo("body");
var dimentions = {
width : temp.outerWidth(true),
height : temp.outerHeight(true)
};
temp.remove();
var overlayProjection = this.getProjection();
var top_left = overlayProjection.fromLatLngToDivPixel(this.point_);
var dimensions= $.extend({}, dimensions, {
y : top_left.y - dimensions.height,
x : top_left.x - dimensions.width/2
});
var div = this.div_;
$(div).css({
"top": dimensions.y + 'px',
"left" : dimensions.x + 'px',
"width" : dimensions.width + 'px',
"height" : dimensions.height + 'px'
}).html(content);
Hope that helps!
希望有帮助!
回答by Henry
You may override the the infobox draw method and reposition the infobox after it has been rendered:
您可以覆盖 infobox draw 方法并在呈现后重新定位 infobox:
var infobox = new InfoBox(myOptions);
infobox.initReady = false;
var oldDraw = infobox.draw;
infobox.draw = function() {
oldDraw.apply(this);
if( ! infobox.initReady) {
// Calculate the required offset
var offsetY = -($(infobox.div_).outerHeight());
var offsetX = -110;
infobox.initReady = true;
// Set the new pixelOffset
infobox.setOptions({
pixelOffset: new google.maps.Size(offsetX, offsetY)
});
}
}