index.html
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>李白一生足迹(701 - 762)</title> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet-minimap@3.6.1/dist/Control.MiniMap.min.css"> <link rel="stylesheet" href="style.css"> <style> /* 全图预览提示 */ .fullmap-tooltip { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.7); color: white; padding: 8px 15px; border-radius: 20px; font-size: 14px; z-index: 900; opacity: 0; transition: opacity 0.5s; } .fullmap-tooltip.show { opacity: 1; } /* 图片查看器(可移动缩放) */ .image-viewer { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; max-width: 1000px; max-height: 80vh; background: rgba(255, 255, 255, 0.95); border-radius: 8px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); z-index: 1000; display: flex; flex-direction: column; opacity: 0; visibility: hidden; transition: opacity 0.3s ease, visibility 0.3s ease; cursor: move; overflow: hidden; } .image-viewer.active { opacity: 1; visibility: visible; } /* 查看器头部(拖拽区域) */ .viewer-header { padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #eee; background: #f0f0f0; cursor: move; } .viewer-title { font-size: 22px; font-weight: bold; } .viewer-close { font-size: 24px; cursor: pointer; transition: transform 0.2s; } .viewer-close:hover { transform: rotate(90deg); } .viewer-content { flex: 1; display: flex; overflow: hidden; cursor: default; } .viewer-image-container { flex: 1; display: flex; justify-content: center; align-items: center; padding: 20px; position: relative; } .viewer-image { max-width: 100%; max-height: 50vh; object-fit: contain; transition: transform 0.3s ease; cursor: move; } .viewer-text { padding: 20px; max-width: 350px; background: #f9f9f9; overflow-y: auto; } .viewer-text h3 { font-size: 20px; margin-bottom: 10px; } .viewer-text p { font-size: 16px; line-height: 1.6; margin-bottom: 15px; } .viewer-text .poem { font-size: 17px; font-style: italic; color: #666; margin-top: 20px; } .viewer-controls { padding: 15px 20px; display: flex; justify-content: center; gap: 15px; border-top: 1px solid #eee; background: #f0f0f0; cursor: default; } .viewer-control { background: #e0e0e0; border: none; padding: 8px 15px; border-radius: 5px; cursor: pointer; transition: background 0.2s; } .viewer-control:hover { background: #d0d0d0; } /* 地图控件样式 */ .leaflet-control button { font-size: 16px; padding: 6px 12px; margin: 2px; } /* 缩略图地点名称 */ .mini-map-label { font-size: 10px; color: #333; text-shadow: 0 1px 0 white; pointer-events: none; } /* 全程路径样式 */ .full-path { z-index: 500 !important; } /* 朗读按钮样式 */ .reader-btn { background-color: #4CAF50; color: white; border: none; padding: 8px 16px; text-align: center; text-decoration: none; display: inline-block; font-size: 14px; margin: 4px 2px; cursor: pointer; border-radius: 4px; transition: background-color 0.3s; } .reader-btn:hover { background-color: #45a049; } .reader-btn.active { background-color: #f44336; } .reader-btn.active:hover { background-color: #d32f2f; } /* 语言选择样式 */ .lang-selector { margin-left: 10px; padding: 5px 10px; border-radius: 4px; border: 1px solid #ccc; background-color: white; } /* 响应式调整 */ @media (max-width: 900px) { .viewer-content { flex-direction: column; } .viewer-text { max-width: 100%; max-height: 30vh; } } </style> </head> <body> <header> <h1>李白一生足迹(701 - 762)</h1> <p>全路径展示 · 可移动查看器 · 语音朗读 · 播放控制</p> </header> <div class="container"> <div class="fullmap-tooltip" id="fullmapTooltip"> 李白一生完整路径已显示 </div> <div id="map"></div> </div> <!-- 可移动缩放的图片查看器 --> <div id="imageViewer" class="image-viewer"> <div class="viewer-header" id="viewerHeader"> <div class="viewer-title" id="viewerTitle">地点名称</div> <div class="viewer-close" id="closeViewer">×</div> </div> <div class="viewer-content"> <div class="viewer-image-container"> <img id="viewerImage" class="viewer-image" src="" alt="风景图"> </div> <div class="viewer-text" id="viewerText"> <h3>标题</h3> <p><strong>年份</strong> | 阶段</p> <p>事件描述</p> <p class="poem">「诗词内容」</p> <p>详细描述</p> </div> </div> <div class="viewer-controls"> <button class="viewer-control" id="zoomIn">图片放大</button> <button class="viewer-control" id="zoomOut">图片缩小</button> <button class="viewer-control" id="resetView">图片重置</button> <button class="viewer-control" id="viewerShrink">框架缩小</button> <button class="viewer-control" id="viewerEnlarge">框架放大</button> <button class="reader-btn" id="startRead">🔊 开始朗读</button> <select class="lang-selector" id="langSelector"> <option value="zh">中文朗读</option> <option value="en">英文朗读</option> </select> </div> </div> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <script src="https://unpkg.com/leaflet-minimap@3.6.1/dist/Control.MiniMap.min.js"></script> <script src="script.js"></script> </body> </html>
script.js`
/* 全局变量 */ let map; let horseMarker; let allPoints = []; let currentStage = 1; let fullPathLayer; let currentImageTransform = { scale: 1, rotate: 0 }; let translateX = 0, translateY = 0; let viewerScale = 1; let isReading = false; let currentLang = 'zh'; let currentPoint = null; // 播放控制变量 let isPlaying = false; // 是否正在播放 let isPaused = false; // 是否处于暂停状态 let currentTimer = null; // 当前计时器ID let currentStageIndex = 0; // 当前阶段索引 let currentPointIndex = 0; // 当前点索引 let allStages = []; // 所有阶段列表 // 中英文翻译映射 const translations = { "长安": { en: "Chang'an", event: "李白在长安为官,写下了许多著名诗篇。", eventEn: "Li Bai served as an official in Chang'an and wrote many famous poems.", poem: "云想衣裳花想容,春风拂槛露华浓。", poemEn: "Her robes are clouds, her face a flower; Spring breeze stirs the railing where she towers." }, "成都": { en: "Chengdu", event: "李白少年时期曾居住于此,开始写诗创作。", eventEn: "Li Bai lived here during his youth and began his poetic career.", poem: "九天开出一成都,万户千门入画图。", poemEn: "Heaven opened to reveal Chengdu; Thousands of homes painted in hues so true." }, "金陵": { en: "Nanjing", event: "李白多次游览金陵,留下众多关于金陵的诗篇。", eventEn: "Li Bai visited Nanjing many times and wrote numerous poems about the city.", poem: "凤凰台上凤凰游,凤去台空江自流。", poemEn: "On Phoenix Terrace phoenixes once flew; Now they're gone, the terrace empty, the river flows on." }, "庐山": { en: "Lushan Mountain", event: "李白游览庐山时,被瀑布的壮观所震撼,写下《望庐山瀑布》。", eventEn: "Li Bai was overwhelmed by the grandeur of Lushan Waterfall and wrote 'Viewing the Waterfall at Lushan Mountain'.", poem: "飞流直下三千尺,疑是银河落九天。", poemEn: "The cataract dashes down three thousand feet; I wonder if the Silver River falls from the sky." }, "扬州": { en: "Yangzhou", event: "李白在扬州度过了一段放荡不羁的时光,结交了许多朋友。", eventEn: "Li Bai spent a carefree time in Yangzhou, making many friends.", poem: "故人西辞黄鹤楼,烟花三月下扬州。", poemEn: "My old friend left the Western Tower; In misty flowers of March for Yangzhou he sails." }, "三峡": { en: "Three Gorges", event: "李白流放途中经过三峡,写下了表达思乡之情的诗篇。", eventEn: "Li Bai passed through the Three Gorges during his exile, writing poems expressing homesickness.", poem: "朝辞白帝彩云间,千里江陵一日还。", poemEn: "Leaving Baidi in the morning clouds; A thousand miles to Jiangling in a day I row." }, "会稽": { en: "Kuaiji", // 这里可以补充相应的 event、eventEn、poem、poemEn 等信息 } }; /* 初始化地图 */ function initMap() { map = L.map('map').setView([35, 105], 4); L.tileLayer('https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', { attribution: '© 高德地图' }).addTo(map); const chinaBounds = L.latLngBounds( L.latLng(18.1, 73.66), L.latLng(53.52, 135.08) ); const miniMapTileLayer = L.tileLayer('https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}'); const miniMap = new L.Control.MiniMap(miniMapTileLayer, { toggleDisplay: true, position: 'bottomright', width: 180, height: 150, zoomLevelFixed: 4, centerFixed: [35, 105], bounds: chinaBounds }).addTo(map); showFullMapPreview(); } /* 显示全路径提示 */ function showFullMapPreview() { const tooltip = document.getElementById('fullmapTooltip'); tooltip.classList.add('show'); setTimeout(() => tooltip.classList.remove('show'), 3000); } /* 加载数据 */ function loadData() { fetch('data.json') .then(r => r.json()) .then(data => { allPoints = data.sort((a, b) => a.year - b.year); allStages = [...new Set(allPoints.map(p => p.stage))].sort((a, b) => a - b); drawFullPath(); drawStages(); createHorse(); createControls(); setupImageViewer(); addMiniMapLabels(); setupReader(); }) .catch(err => { console.error('数据加载失败:', err); alert('请确保data.json文件在正确位置'); }); } /* 绘制全程路径 */ function drawFullPath() { const allCoords = allPoints.map(p => p.coord); fullPathLayer = L.polyline(allCoords, { color: '#c0392b', weight: 6, opacity: 0.9, dashArray: '12, 6', lineJoin: 'round', className: 'full-path' }).addTo(map); } /* 绘制阶段连线 */ function drawStages() { const stageColors = [ '#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c', '#e67e22', '#34495e' ]; for (let s = 1; s <= 8; s++) { const pts = allPoints.filter(p => p.stage === s); const coords = pts.map(p => p.coord); L.polyline(coords, { color: stageColors[s - 1], weight: 3, opacity: 0.8 }).addTo(map); pts.forEach(p => { const marker = L.circleMarker(p.coord, { radius: 8, color: stageColors[s - 1], fillColor: '#fff', fillOpacity: 1, stroke: true, weight: 2 }).addTo(map); marker.on('click', () => { currentPoint = p; openImageViewer(p); map.setView(p.coord, 10); }); marker.bindTooltip(`${p.name}(${p.year})`); }); } } /* 创建马 */ function createHorse() { const horseIcon = L.icon({ iconUrl: 'horse.png', iconSize: [32, 32], iconAnchor: [16, 32], popupAnchor: [0, -32] }); horseMarker = L.marker(allPoints[0].coord, { icon: horseIcon }).addTo(map); } /* 控制按钮:用原生 DOM 避免兼容问题 */ function createControls() { const div = L.DomUtil.create('div', 'leaflet-bar leaflet-control'); div.style.background = '#fff'; div.style.padding = '4px'; div.innerHTML = ` <button id="btnPlay">▶ 阶段 ${currentStage}</button> <button id="btnReset">↺ 重置</button>`; document.body.appendChild(div); L.DomEvent.disableClickPropagation(div); document.getElementById('btnPlay').addEventListener('click', playStage); document.getElementById('btnReset').addEventListener('click', resetHorse); } /* 动画播放 */ function playStage() { const pts = allPoints.filter(p => p.stage === currentStage); if (!pts.length) { alert('已到最后阶段'); return; } let i = 0; const move = () => { if (i >= pts.length) { currentStage++; updateBtn(); return; } const p = pts[i]; horseMarker.setLatLng(p.coord); map.setView(p.coord, 10, { animate: true, duration: 1 }); horseMarker.bindPopup(` <strong>${p.name}</strong>(${p.year})<br> ${p.event}<br> <em>“${p.poem}”</em> `).openPopup(); i++; setTimeout(move, 2500); }; move(); } function resetHorse() { currentStage = 1; horseMarker.setLatLng(allPoints[0].coord); map.setView([34, 110], 5); updateBtn(); } function updateBtn() { document.getElementById('btnPlay').textContent = `▶ 阶段 ${currentStage}`; } /* 打开图片查看器 */ function openImageViewer(point) { const viewer = document.getElementById('imageViewer'); const title = document.getElementById('viewerTitle'); const image = document.getElementById('viewerImage'); const text = document.getElementById('viewerText'); currentImageTransform = { scale: 1, rotate: 0 }; translateX = 0; translateY = 0; viewerScale = 1; viewer.style.transform = 'translate(-50%, -50%) scale(1)'; // 设置内容 title.textContent = point.name; image.src = point.image || `https://picsum.photos/800/600?random=${point.year}&keyword=${encodeURIComponent(point.name)}`; image.alt = `${point.name}风景图`; // 根据当前语言设置文本 if (currentLang === 'zh') { text.innerHTML = ` <h3>${point.name}</h3> <p><strong>${point.year}年</strong> | 第${point.stage}阶段</p> <p>${point.event}</p> <p class="poem">「${point.poem}」</p> ${point.description ? `<p>${point.description}</p>` : ''} `; } else { const trans = translations[point.name] || { en: point.name, eventEn: point.event, poemEn: point.poem }; text.innerHTML = ` <h3>${trans.en}</h3> <p><strong>Year ${point.year}</strong> | Stage ${point.stage}</p> <p>${trans.eventEn}</p> <p class="poem">"${trans.poemEn}"</p> ${point.descriptionEn ? `<p>${point.descriptionEn}</p>` : ''} `; } viewer.classList.add('active'); } /* 初始化朗读功能 */ function setupReader() { const startReadBtn = document.getElementById('startRead'); const langSelector = document.getElementById('langSelector'); startReadBtn.addEventListener('click', () => { if (isReading) { stopReading(); } else { const textToRead = currentLang === 'zh' ? document.getElementById('viewerText').textContent : translations[currentPoint.name]?.eventEn || currentPoint.event; startReading(textToRead); } }); langSelector.addEventListener('change', (e) => { currentLang = e.target.value; if (currentPoint) { openImageViewer(currentPoint); } }); } /* 开始朗读 */ function startReading(textToRead) { const startReadBtn = document.getElementById('startRead'); startReadBtn.textContent = "🔊 停止朗读"; startReadBtn.classList.add('active'); isReading = true; // 创建语音合成实例 const utterance = new SpeechSynthesisUtterance(textToRead); // 设置语言 utterance.lang = currentLang === 'zh' ? 'zh-CN' : 'en-US'; // 设置语速和音调 utterance.rate = 0.9; utterance.pitch = 1; // 朗读结束回调 utterance.onend = () => { stopReading(); }; utterance.onerror = (err) => { console.error('朗读错误:', err); stopReading(); alert('朗读功能在当前浏览器中可能不支持'); }; // 开始朗读 window.speechSynthesis.speak(utterance); } /* 停止朗读 */ function stopReading() { const startReadBtn = document.getElementById('startRead'); startReadBtn.textContent = "🔊 开始朗读"; startReadBtn.classList.remove('active'); isReading = false; window.speechSynthesis.cancel(); } /* 页面加载完成后初始化 */ document.addEventListener('DOMContentLoaded', () => { initMap(); map.whenReady(() => loadData()); });
style.css
* { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "SimSun", serif; background:#f5f5f5; color:#333; } header { background:#0d1b2a; color:#fff; padding:20px; text-align:center; } #map { height: 70vh; }
data.json
[ {"name":"碎叶城","coord":[75.3000,42.8333],"year":701,"stage":1,"event":"出生西域,幼名李客之子。","poem":"白云在天,丘陵自出。","description":"李白出生于碎叶城(今吉尔吉斯斯坦托克马克附近),父亲李客为富商。幼年时期,他接触到西域文化,养成豪放不羁的性格。","image":"images/suiye.jpg"}, {"name":"绵州昌隆县","coord":[104.6833,31.7667],"year":705,"stage":1,"event":"五岁随父入蜀,少年学剑读书。","poem":"仰天大笑出门去,我辈岂是蓬蒿人。","description":"李白五岁随父迁居蜀地,少年时期在昌隆县(今四川江油)度过。他遍览经史子集,学剑术任侠,常与山中隐者交往,种下了道家思想的种子。","image":"images/changlong.jpg"}, {"name":"成都","coord":[104.0700,30.6700],"year":715,"stage":1,"event":"负笈游成都,击剑任侠。","poem":"蜀道之难,难于上青天。","description":"李白十五岁游成都,拜谒文坛名士,作赋《大鹏遇希有鸟赋》自比大鹏。此时他已显露诗才,但因不屑科举,决心仗剑远游,探索广阔天地。","image":"images/chengdu.jpg"}, {"name":"峨眉山","coord":[103.4170,29.5997],"year":720,"stage":1,"event":"登峨眉,作《峨眉山月歌》。","poem":"峨眉山月半轮秋,影入平羌江水流。","description":"李白十九岁隐居峨眉山,与道士东严子同隐,养奇禽千计。他在此饱览蜀中山水,写下“峨眉山月半轮秋”的名句,为日后的壮游积累了灵感。","image":"images/emeishan.jpg"}, {"name":"渝州","coord":[106.5500,29.5500],"year":725,"stage":2,"event":"仗剑出蜀,辞亲远游。","poem":"朝辞白帝彩云间,千里江陵一日还。","description":"开元十三年,李白“仗剑去国,辞亲远游”,乘舟出三峡。他带着蜀中少年的豪迈,怀揣“申管晏之谈,谋帝王之术”的抱负,开始了长达十六年的壮游生涯。","image":"images/yuzhou.jpg"}, {"name":"江陵","coord":[112.2000,30.3500],"year":725,"stage":2,"event":"初到荆州,放舟洞庭。","poem":"渡远荆门外,来从楚国游。","description":"李白出蜀后首抵江陵,结识道士司马承祯,获赞“有仙风道骨,可与神游八极之表”。他深受鼓舞,沿江东下,开始领略楚地风光,诗风渐趋壮阔。","image":"images/jiangling.jpg"}, {"name":"岳阳","coord":[113.1000,29.3500],"year":726,"stage":2,"event":"洞庭留别,浪迹潇湘。","poem":"且就洞庭赊月色,将船买酒白云边。","description":"李白在洞庭湖泛舟时,听闻好友吴指南病逝,悲痛欲绝,“泣尽继之以血”。他守丧一年,亲自营葬,展现出重情重义的一面。之后继续南游潇湘,留下多篇纪行诗。","image":"images/yueyang.jpg"}, {"name":"庐山","coord":[115.9833,29.5833],"year":727,"stage":2,"event":"望庐山瀑布,豪情万丈。","poem":"飞流直下三千尺,疑是银河落九天。","description":"李白初见庐山瀑布,被其壮丽震撼,挥笔写下千古绝唱《望庐山瀑布》。此时的他正值青春年少,诗中洋溢着对自然的赞美和对自由的向往,尽显浪漫主义风采。","image":"images/lushan.jpg"}, {"name":"金陵","coord":[118.8000,32.0500],"year":728,"stage":2,"event":"金陵酒肆,风流少年。","poem":"凤凰台上凤凰游,凤去台空江自流。","description":"李白漫游至金陵,沉醉于六朝古都的繁华与沧桑。他常与友人在秦淮河畔饮酒赋诗,写下《金陵酒肆留别》等佳作,尽显少年风流。此时他虽未入仕途,但诗名已渐传江南。","image":"images/jinling.jpg"}, {"name":"扬州","coord":[119.4000,32.4000],"year":729,"stage":2,"event":"广陵散金,结交豪雄。","poem":"故人西辞黄鹤楼,烟花三月下扬州。","description":"李白在扬州“散金三十万,有落魄公子,悉皆济之”,尽显任侠气概。他在此结交了不少豪雄俊杰,开阔了视野,但也因生活挥霍,不久便钱财散尽,开始反思人生道路。","image":"images/yangzhou.jpg"}, {"name":"越州","coord":[120.6000,30.0000],"year":730,"stage":2,"event":"天台访仙,乘兴而返。","poem":"天台四万八千丈,对此欲倒东南倾。","description":"李白溯江而上,漫游吴越,足迹遍布会稽、天台等地。他寻访谢灵运遗迹,与道士谈玄论道,深受道家思想影响。这一时期的经历,为他日后创作《梦游天姥吟留别》积累了素材。","image":"images/yuezhou.jpg"}, {"name":"安陆","coord":[113.6833,31.2500],"year":730,"stage":3,"event":"与许氏成婚,暂居安陆。","poem":"桃花流水窅然去,别有天地非人间。","description":"李白在安陆与故相许圉师孙女成婚,定居十年。这期间他以安陆为中心,四处干谒求仕,却屡屡碰壁。他寄情山水,写下《山中问答》等诗,表面闲适,实则暗藏怀才不遇的苦闷。","image":"images/anlu.jpg"}, {"name":"襄阳","coord":[112.1500,32.0000],"year":733,"stage":3,"event":"访孟浩然,诗酒唱和。","poem":"吾爱孟夫子,风流天下闻。","description":"李白结识孟浩然,两人一见如故,结伴漫游。李白对孟浩然的人品和诗才极为推崇,写下“吾爱孟夫子,风流天下闻”的赞语。此次相遇,成为中国文学史上的一段佳话。","image":"images/xiangyang.jpg"}, {"name":"长安","coord":[108.9500,34.2667],"year":733,"stage":3,"event":"初谒仕途,失意而返。","poem":"长安不见使人愁,长啸梁甫吟。","description":"李白首次入长安,试图通过献赋、干谒等方式进入仕途,却遭权贵冷遇。他在长安目睹了宫廷的腐败和社会的矛盾,写下《蜀道难》等诗,以奇幻的想象抒发对人生道路的迷茫与感慨。","image":"images/changan.jpg"}, {"name":"任城","coord":[116.6000,35.4000],"year":736,"stage":4,"event":"移家东鲁,隐居徂徕。","poem":"兰陵美酒郁金香,玉碗盛来琥珀光。","description":"李白举家迁居东鲁,与孔巢父等隐于徂徕山,号“竹溪六逸”。他在此饮酒作乐,啸傲山林,看似逍遥自在,实则仍心系天下,渴望有朝一日能实现政治抱负。","image":"images/rencheng.jpg"}, {"name":"曲阜","coord":[116.9833,35.6000],"year":737,"stage":4,"event":"问礼孔庙,儒风入怀。","poem":"我本楚狂人,凤歌笑孔丘。","description":"李白游览曲阜,拜谒孔庙,对儒家思想有了更深理解。尽管他常以“楚狂人”自比,但内心仍有“致君尧舜”的儒家理想。这种矛盾贯穿了他的一生,也体现在他的诗歌创作中。","image":"images/qufu.jpg"}, {"name":"泰山","coord":[117.1000,36.2000],"year":740,"stage":4,"event":"登岱宗而小天下。","poem":"会当凌绝顶,一览众山小。","description":"李白登临泰山,被其雄伟气势震撼,写下《游泰山六首》。他在诗中描绘了泰山的壮丽景色和道教神话,表达了对神仙世界的向往,也流露出对现实的不满。","image":"images/taishan.jpg"}, {"name":"嵩山","coord":[112.9667,34.4833],"year":742,"stage":4,"event":"待诏阙下,心向紫宸。","poem":"闲来垂钓碧溪上,忽复乘舟梦日边。","description":"李白与杜甫、高适同游梁宋,三人登高怀古,饮酒赋诗,结下深厚友谊。这一时期,李白的诗歌风格更加成熟,既有“天生我材必有用”的自信,也有“抽刀断水水更流”的惆怅。","image":"images/songshan.jpg"}, {"name":"长安","coord":[108.9500,34.2667],"year":742,"stage":5,"event":"供奉翰林,醉草《清平调》。","poem":"云想衣裳花想容,春风拂槛露华浓。","description":"因道士吴筠推荐,李白奉诏入长安,为翰林待诏。起初,他深受玄宗赏识,曾为杨贵妃作诗《清平调》三首。但他不满于只做“文学弄臣”,常饮酒纵乐,得罪权贵,三年后便弃官离京。","image":"images/changan.jpg"}, {"name":"骊山","coord":[109.2167,34.3667],"year":743,"stage":5,"event":"侍从温泉宫,醉卧金銮。","poem":"名花倾国两相欢,长得君王带笑看。","description":"李白常随玄宗游幸骊山温泉宫,写下《侍从游宿温泉宫作》等诗。表面上,他过着“承恩初入银台门,著书独在金銮殿”的荣耀生活,但实际上,他对宫廷的奢靡和政治的黑暗深感失望。","image":"images/lishan.jpg"}, {"name":"洛阳","coord":[112.4500,34.6167],"year":744,"stage":6,"event":"洛阳重逢杜甫,千古唱和。","poem":"痛饮狂歌空度日,飞扬跋扈为谁雄。","description":"李白与杜甫在洛阳相遇,两位文学巨匠一见如故,同游梁宋、齐鲁。他们一起饮酒赋诗,论道谈玄,结下了深厚的友谊。这段经历对两人的诗歌创作都产生了深远影响。","image":"images/luoyang.jpg"}, {"name":"开封","coord":[114.3500,34.8000],"year":745,"stage":6,"event":"梁园吟咏,高会吹台。","poem":"昔为大梁客,不负信陵恩。","description":"李白与杜甫、高适同游梁园(今开封),登吹台饮酒赋诗。李白在此写下《梁园吟》,抒发怀才不遇的悲愤,同时表达了对自由生活的向往。这次聚会成为中国文学史上的一段佳话。","image":"images/kaifeng.jpg"}, {"name":"宣城","coord":[118.7500,30.9500],"year":753,"stage":6,"event":"谢朓楼上,长歌当哭。","poem":"抽刀断水水更流,举杯消愁愁更愁。","description":"安史之乱前,李白辗转于宣城、金陵等地,写下《宣州谢朓楼饯别校书叔云》等名篇。此时他已年近五旬,仕途失意,亲友离散,诗中充满了对人生苦难的深刻体验和对理想的执着追求。","image":"images/xuancheng.jpg"}, {"name":"庐山","coord":[115.9833,29.5833],"year":756,"stage":7,"event":"入永王幕,志在勤王。","poem":"南风一扫胡尘静,西入长安到日边。","description":"安史之乱爆发后,李白隐居庐山。永王李璘出师东巡,三次征召,李白应邀入幕,以为终于有机会实现政治抱负。他写下《永王东巡歌》十一首,表达了“但用东山谢安石,为君谈笑静胡沙”的壮志。","image":"images/lushan.jpg"}, {"name":"浔阳","coord":[115.9833,29.7000],"year":757,"stage":7,"event":"兵败系狱,长流夜郎。","poem":"平生不下泪,于此泣无穷。","description":"永王李璘案,被流放夜郎,后遇赦东还。他辗转于金陵、宣城等地,生活困顿,疾病缠身。尽管如此,他仍心系国家,写下《闻官军收河南河北》等诗,表达对平定叛乱的渴望。","image":"images/xunyang.jpg"}, {"name":"白帝城","coord":[109.5833,31.0500],"year":759,"stage":8,"event":"遇赦东还,轻舟一日千里。","poem":"朝辞白帝彩云间,千里江陵一日还。","description":"行至白帝城的时候,忽然收到赦免的消息,惊喜交加,随即乘舟东下江陵。在《早发白帝城》中,他以轻快的笔触表达了获赦后的喜悦心情和对自由的向往。","image":"images/baidicheng.jpg"}, {"name":"当涂","coord":[118.4833,31.5500],"year":762,"stage":8,"event":"病逝青山,诗魂长留。","poem":"屈平词赋悬日月,楚王台榭空山丘。","description":"李白病重,将手稿托付给当涂县令李阳冰。同年十一月,病逝,享年六十二岁。他的诗歌风格豪放飘逸、意境奇妙,充满浪漫主义色彩,被后人誉为“诗仙”,与杜甫并称“李杜”。","image":"images/dangtu.jpg"} ]
文件说明:
将上述代码保存到对应的文件中。
确保 horse.png 文件存在于项目根目录下。
打开命令行工具,进入项目目录。
cmd
cd C:/Users/DELL/Desktop/唐诗宋词/李白一生踪迹
运行 npx serve 启动本地服务器。
打开浏览器,访问 http://localhost:56428/ 查看效果或者
http://localhost:3000
手动下载图片方案
若百度 API 仍无法获取图片,可手动下载真实风景图并关联:
图片来源推荐
星图地球数据云:提供 0.5 米分辨率中国城市影像(参考(https://cloud.tencent.com/developer/article/2479161))
汇图网:风景全景摄影素材(参考(https://www.huitu.com:10443/topic/detail/5050.html))
维基百科:搜索地点名称,进入页面后右键保存图片
关联图片到代码
在项目根目录创建images文件夹,存放下载的图片(如emeishan.jpg)
在data.json中添加image字段:
json
{"name":"峨眉山","image":"images/emeishan.jpg","coord":[103.4170,29.5997],"year":720,"stage":1,"event":"登峨眉,作《峨眉山月歌》。","poem":"峨眉山月半轮秋,影入平羌江水流。","description":"李白十九岁隐居峨眉山,与道士东严子同隐,养奇禽千计。他在此饱览蜀中山水,写下“峨眉山月半轮秋”的名句,为日后的壮游积累了灵感。"}
修改代码中的图片加载逻辑:
javascript
// 在playStage函数中,优先使用本地图片
const imageUrl = p.image || streetViewUrl || wikiImageUrl;