Basemap Switch
Swap street, light and dark basemaps without losing markers or overlays.
single HTML file · CDN dependencies onlyinterface may contain Chinese labels
Source
<!--
示例:多底图切换
要点:三套公开瓦片、保留中心与缩放、版权信息随图层自动更换、按钮组互斥
依赖:Leaflet 1.9.4 · jsDelivr CDN + Esri 公共瓦片
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="robots" content="noindex,follow">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>多底图切换</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css">
<style>
html,body{height:100%;margin:0}
body{display:flex;flex-direction:column;overflow:hidden;
font:13px/1.6 -apple-system,"Segoe UI","PingFang SC",sans-serif}
.bar{flex:0 0 auto;padding:8px 12px;background:#f6f7f9;border-bottom:1px solid #e3e6ea;
display:flex;gap:8px;align-items:center;font-size:12px;color:#58606e}
.bar .lab{font-weight:600;color:#16181d}
.bar button{font:inherit;font-size:12px;padding:4px 12px;border-radius:4px;cursor:pointer;
border:1px solid #ccd1d9;background:#fff;color:#16181d}
.bar button:hover{background:#eef0f3}
.bar button.is-on{background:#1f5fd0;border-color:#1f5fd0;color:#fff}
.bar .spacer{flex:1 1 auto}
#map{flex:1 1 auto;min-height:0}
.pin{width:14px;height:14px;border-radius:50%;background:#c2410c;
border:2px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,.25)}
</style>
</head>
<body>
<div class="bar">
<span class="lab">底图</span>
<button type="button" class="base is-on" data-base="osm">街道</button>
<button type="button" class="base" data-base="light">浅色</button>
<button type="button" class="base" data-base="dark">深色</button>
<span class="spacer"></span>
<span id="status">缩放 12</span>
</div>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
(function () {
'use strict';
var map = L.map('map', {
center: [39.907359, 116.391263],
zoom: 12
});
var layers = {
osm: L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
maxZoom: 19,
attribution: 'Tiles © Esri'
}),
light: L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
subdomains: 'abcd',
maxZoom: 20,
attribution: 'Tiles © Esri'
}),
dark: L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
subdomains: 'abcd',
maxZoom: 20,
attribution: 'Tiles © Esri'
})
};
var current = 'osm';
layers[current].addTo(map);
/* 一个标记与一个圆,验证切换底图时上层内容不受影响 */
L.circle([39.907359, 116.391263], {
radius: 1200,
color: '#c2410c',
weight: 1,
fillColor: '#c2410c',
fillOpacity: 0.1
}).addTo(map);
L.marker([39.907359, 116.391263], {
icon: L.divIcon({
className: '',
html: '<div class="pin"></div>',
iconSize: [14, 14],
iconAnchor: [7, 7]
})
}).bindPopup('<b>切换底图不会清除上层图层</b>').addTo(map);
var buttons = document.querySelectorAll('.base');
var status = document.getElementById('status');
function markActive(name) {
for (var i = 0; i < buttons.length; i++) {
buttons[i].classList.toggle('is-on', buttons[i].getAttribute('data-base') === name);
}
}
function switchBase(name) {
if (name === current || !layers[name]) return;
/* 先移除旧底图:版权信息与瓦片请求会一起清掉 */
map.removeLayer(layers[current]);
map.addLayer(layers[name]);
current = name;
markActive(name);
status.textContent = '已切换 · 缩放 ' + map.getZoom();
}
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function () {
switchBase(this.getAttribute('data-base'));
});
}
map.on('zoomend', function () {
status.textContent = '缩放 ' + map.getZoom();
});
})();
</script>
</body>
</html>
Language: the code and this page are in English; the full tutorial and API notes for this example are in Chinese. The demo itself is the authoritative version — it runs exactly as shown.