Google Maps 預設 google.maps.Marker 僅支援圖片圖示,無法直接顯示文字。透過繼承 OverlayView (或 v3 中擴展 Marker) 實作 LabelMarker,即可在地圖上直接繪製文字標籤 (如地名、編號),無需產生圖片。
google.maps.LabelMarker = function(latlng, options) {
this.latlng = latlng;
this.labelText = options.labelText || '';
this.labelClass = options.labelClass || 'map-label'; // CSS 類別
this.labelOffset = options.labelOffset || new google.maps.Size(8, -33);
options.icon = options.icon || this.getTransparentIcon(); // 透明圖示佔位
google.maps.Marker.apply(this, arguments);
};
// 繼承 Marker
google.maps.LabelMarker.prototype = new google.maps.Marker(new google.maps.LatLng(0, 0));
// 建立 1x1 透明圖示避免預設圖示干擾
google.maps.LabelMarker.prototype.getTransparentIcon = function() {
var icon = {
url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
size: new google.maps.Size(1, 1),
anchor: new google.maps.Point(0, 1)
};
return icon;
};
// 初始化:建立 DOM 標籤元素
google.maps.LabelMarker.prototype.setMap = function(map) {
google.maps.Marker.prototype.setMap.call(this, map);
if (this.labelDiv) return; // 已初始化
this.labelDiv = document.createElement('div');
this.labelDiv.className = this.labelClass;
this.labelDiv.innerHTML = this.labelText;
this.labelDiv.style.position = 'absolute';
this.labelDiv.style.whiteSpace = 'nowrap';
this.labelDiv.style.pointerEvents = 'none'; // 穿透點擊
// 加入 MAP_MARKER_PANE (v3 對應 overlayMouseTarget 或 overlayLayer)
var panes = this.getPanes();
if (panes) panes.overlayLayer.appendChild(this.labelDiv);
};
// 重繪:依投影座標更新標籤位置
google.maps.LabelMarker.prototype.draw = function() {
if (!this.labelDiv) return;
var projection = this.getProjection();
if (!projection) return;
var pixel = projection.fromLatLngToDivPixel(this.latlng);
if (!pixel) return;
this.labelDiv.style.left = (pixel.x + this.labelOffset.width) + 'px';
this.labelDiv.style.top = (pixel.y + this.labelOffset.height) + 'px';
this.labelDiv.style.zIndex = Math.round(this.latlng.lat() * -100000) + 1; // 緯度越高 zIndex 越大
};
// 移除:清理 DOM
google.maps.LabelMarker.prototype.onRemove = function() {
if (this.labelDiv && this.labelDiv.parentNode) {
this.labelDiv.parentNode.removeChild(this.labelDiv);
}
this.labelDiv = null;
};
.map-label {
font-family: 'Microsoft JhengHei', Arial, sans-serif;
font-size: 12px;
font-weight: bold;
color: #333;
background: rgba(255,255,255,0.9);
padding: 2px 6px;
border-radius: 3px;
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
border: 1px solid #ccc;
}
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 25.033, lng: 121.565},
zoom: 12
});
var marker = new google.maps.LabelMarker(map.getCenter(), {
labelText: '我在這裡',
labelClass: 'map-label',
labelOffset: new google.maps.Size(10, -35)
});
marker.setMap(map);
}
marker.labelDiv.innerHTML = '新文字';pointerEvents: 'none' 讓點擊事件傳給底層 Marker/地圖MarkerClusterer 或自行演算法調整 labelOffsetinnerHTML 可放入任意 HTML (圖示+文字、超連結等)| 特性 | API v2 (舊) | API v3 (現行) |
|---|---|---|
| 基底類別 | GMarker / GOverlay | google.maps.Marker / OverlayView |
| 投影轉換 | map.fromLatLngToDivPixel() | projection.fromLatLngToDivPixel() |
| Pane 常數 | G_MAP_MARKER_PANE | overlayLayer / overlayMouseTarget |
| 初始化鉤子 | initialize(map) | setMap(map) |
| 繪製鉤子 | redraw(force) | draw() |
本文代碼為 **API v3** 版本,v2 專案請參考原始碼調整對應方法名與常數。