杜甫的一生,就是一部盛唐到中唐的变迁史
杜甫的一生,就是一部盛唐到中唐的变迁史。
20岁开始漫游吴越,24岁进士落第后游齐赵,"会当凌绝顶,一览众山小";
35岁困守长安十年,目睹"朱门酒肉臭,路有冻死骨";
44岁安史之乱爆发,携家逃难,写下"国破山河在,城春草木深";
48岁入蜀,在成都建草堂,"两个黄鹂鸣翠柳,一行白鹭上青天";
57岁出蜀东下,漂泊荆湘,"无边落木萧萧下,不尽长江滚滚来";
59岁病逝湘江舟中,诗圣陨落。
从长安到成都,从洛阳到潭州,杜甫用脚步丈量了唐王朝的兴衰。
他的诗,是那个时代最真实的见证。
#杜甫 #诗圣 #唐诗中国 (点击图片可了解杜甫一生行迹)
网站源代码共享
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="沉浸式体验杜甫一生的行迹与诗歌创作历程">
<title>诗圣杜甫一生行迹 - 沉浸式文化体验</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet-ant-path@1.3.0/dist/leaflet-ant-path.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
background-color: #f5f5f5;
line-height: 1.6;
color: #333;
overflow-x: hidden;
}
header {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: rgba(255, 255, 255, 0.95);
padding: 15px;
text-align: center;
font-size: 24px;
font-weight: bold;
color: #333;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 1000;
backdrop-filter: blur(5px);
}
#mapLoader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: white;
z-index: 9999;
transition: opacity 0.5s ease-out;
}
.loader-spinner {
width: 50px;
height: 50px;
border: 5px solid #f3f3f3;
border-top: 5px solid #8B4513;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#map {
width: 100vw;
height: 100vh;
position: relative;
margin-top: 60px;
}
/* 信息卡片样式 - 优化透明度和布局以避免遮挡路径 */
.info-card {
position: fixed;
bottom: 40px;
left: 50%;
transform: translateX(-50%) translateY(100%);
width: 80%;
max-width: 1200px;
background-color: rgba(255, 255, 255, 0.5); /* 增加透明度到50% */
backdrop-filter: blur(12px);
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
transition: transform 0.5s ease, max-height 0.5s ease, opacity 0.5s ease;
z-index: 900;
display: flex;
flex-direction: row;
overflow: hidden;
cursor: pointer;
opacity: 0;
}
.info-card.active {
opacity: 1;
}
/* 折叠按钮样式 */
.collapse-btn {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
width: 40px;
height: 20px;
background-color: rgba(255, 255, 255, 0.8);
border: none;
border-radius: 10px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
z-index: 10;
}
.collapse-btn:hover {
background-color: rgba(255, 255, 255, 1);
}
.collapse-btn::before {
content: '⌄';
transition: transform 0.3s ease;
}
.info-card.collapsed .collapse-btn::before {
transform: rotate(180deg);
}
/* 折叠状态的信息卡片 */
.info-card.collapsed {
max-height: 100px;
}
.info-card.collapsed .info-content {
max-height: 60px;
}
/* 马标志样式 */
.horse-marker {
font-size: 40px !important; /* 增加马标志的尺寸,使其更加醒目 */
text-shadow: 0 3px 6px rgba(0, 0, 0, 0.4);
transform-origin: center center;
transition: transform 0.3s ease;
animation: horseFloat 3s ease-in-out infinite;
}
@keyframes horseFloat {
0% { transform: translateY(0px); }
50% { transform: translateY(-5px); }
100% { transform: translateY(0px); }
}
.horse-marker:hover {
transform: scale(1.3) rotate(5deg);
}
.info-card.active {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
/* 独立的图片区域 */
.info-image {
width: 25%;
min-width: 150px;
height: 300px; /* 增加图片高度 */
object-fit: cover;
position: relative;
border-radius: 15px 0 0 15px;
transition: filter 0.3s ease;
}
.info-image:hover {
filter: brightness(1.1);
}
/* 图片加载动画 */
.image-loading::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 50%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
to {
left: 100%;
}
}
/* 独立的内容区域 */
.info-content {
padding: 20px;
width: 75%;
overflow-y: auto;
max-height: 300px;
position: relative;
transition: max-height 0.5s ease;
}
/* 内容滚动条样式优化 */
.info-content::-webkit-scrollbar {
width: 8px;
}
.info-content::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.info-content::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.info-content::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3);
}
/* 滚动内容容器 */
.scroll-container {
height: 100%;
overflow-y: auto;
}
/* 滚动控制按钮 */
.scroll-control {
position: absolute;
bottom: 10px;
right: 10px;
display: flex;
gap: 5px;
}
.scroll-btn {
width: 30px;
height: 30px;
border-radius: 50%;
border: none;
background-color: rgba(255, 255, 255, 0.8);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
transition: background-color 0.3s;
}
.scroll-btn:hover {
background-color: rgba(255, 255, 255, 1);
}
.info-title {
font-size: 36px;
margin-bottom: 12px;
color: #333;
text-align: center;
font-weight: bold;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1);
letter-spacing: 0.5px;
}
.info-year {
font-size: 16px;
color: #666;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
text-align: center;
font-style: italic;
}
.info-description {
margin-bottom: 20px;
line-height: 2.0;
font-size: 25px;
color: #333;
text-indent: 2em;
letter-spacing: 0.8px;
}
/* 优化诗句样式 */
.info-poem {
color: #8B4513;
margin-bottom: 15px;
padding: 15px;
background-color: #FFF8DC;
border-radius: 10px;
font-style: italic;
border-left: 4px solid #8B4513;
box-shadow: 0 2px 8px rgba(139, 69, 19, 0.1);
}
/* 双语文字样式优化 */
.bilingual-text {
margin-bottom: 10px;
}
.chinese-text {
font-family: 'Microsoft YaHei', 'PingFang SC', serif;
font-size: 25px;
color: #333;
line-height: 2.0;
margin-bottom: 12px;
letter-spacing: 0.8px;
}
.english-text {
font-family: 'Times New Roman', serif;
font-size: 20px;
color: #555;
line-height: 1.8;
font-style: italic;
letter-spacing: 0.5px;
}
.bilingual-item {
margin-bottom: 15px;
}
.lang-label {
font-weight: bold;
color: #8B4513;
margin-bottom: 5px;
font-size: 14px;
}
/* 标题动画效果 */
.title-animation {
animation: fadeInScale 1s ease-out;
}
@keyframes fadeInScale {
0% { opacity: 0; transform: scale(0.9); }
100% { opacity: 1; transform: scale(1); }
}
/* 高亮诗句样式 */
.poem-highlight {
background-color: rgba(139, 69, 19, 0.05);
padding: 10px;
border-radius: 8px;
border-left: 4px solid #8B4513;
}
.info-close {
position: absolute;
top: 10px;
right: 10px;
width: 30px;
height: 30px;
background-color: rgba(0, 0, 0, 0.2);
color: white;
border: none;
border-radius: 50%;
font-size: 18px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.3s;
}
.info-close:hover {
background-color: rgba(0, 0, 0, 0.4);
}
/* 控制按钮 */
.controls {
position: fixed;
top: 100px;
right: 20px;
display: flex;
flex-direction: column;
gap: 10px;
z-index: 1050;
transition: transform 0.3s ease;
}
/* 控制按钮提示 */
.control-btn::after {
content: attr(data-tooltip);
position: absolute;
right: 100%;
margin-right: 10px;
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 5px 10px;
border-radius: 5px;
font-size: 12px;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s, visibility 0.3s;
}
.control-btn:hover::after {
opacity: 1;
visibility: visible;
}
.control-btn {
width: 50px;
height: 50px;
border-radius: 50%;
border: none;
background-color: rgba(255, 255, 255, 0.9);
color: #333;
font-size: 20px;
cursor: pointer;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
position: relative;
backdrop-filter: blur(5px);
}
.control-btn:hover {
background-color: #fff;
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
}
/* 阶段颜色指示器 */
.stage-indicator {
position: fixed;
top: 100px;
left: 0;
right: 0;
background-color: rgba(255, 255, 255, 0.95);
padding: 10px 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
z-index: 800;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 15px;
backdrop-filter: blur(5px);
transition: transform 0.3s ease;
}
.stage-title {
font-weight: bold;
color: #333;
margin-right: 15px;
}
.stage-colors {
display: flex;
flex-wrap: wrap;
gap: 15px;
justify-content: center;
}
.stage-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}
.stage-color {
width: 20px;
height: 20px;
border-radius: 4px;
border: 1px solid #ddd;
}
/* 语言显示区域 - 调整为同时显示双语 */
.lang-label {
text-align: right;
font-size: 12px;
color: #999;
margin-bottom: 5px;
}
.bilingual-item {
margin-bottom: 15px;
}
.bilingual-text {
display: flex;
flex-direction: column;
gap: 5px;
}
.chinese-text {
font-size: 16px;
color: #333;
}
.english-text {
font-size: 14px;
color: #666;
font-style: italic;
}
/* 移除语言切换按钮 */
.lang-switch {
display: none;
}
/* 回到顶部按钮 */
.back-to-top {
position: fixed;
bottom: 20px;
right: 20px;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.9);
color: #333;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s, visibility 0.3s, transform 0.3s;
z-index: 950;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(5px);
}
.back-to-top.visible {
opacity: 1;
visibility: visible;
}
.back-to-top:hover {
transform: translateY(-3px);
background-color: white;
}
/* 加载进度条 */
.progress-bar {
position: fixed;
top: 60px;
left: 0;
height: 4px;
background: linear-gradient(90deg, #8B4513, #A0522D, #CD853F, #DEB887);
width: 0%;
z-index: 1001;
transition: width 0.3s ease;
}
/* 适配移动端 */
@media(max-width:768px){
header {
font-size: 20px;
padding: 12px;
}
.info-card {flex-direction:column; left: 5%; right: 5%;}
.info-image {width:100%;height:200px}
.info-content {max-height:40vh; width: 100%;}
.controls {top: auto; bottom: 150px; right: 20px; flex-direction: row;}
.stage-indicator {top: auto; bottom: 220px; left: 20px; right: 20px;}
.stage-colors {flex-direction: row; flex-wrap: wrap;}
/* 移动端触摸优化 */
.control-btn {
width: 60px;
height: 60px;
font-size: 24px;
}
}
</style>
</head>
<body>
<header>
<div style="font-size: 36px; font-weight: bold; color: #333; margin-bottom: 5px; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);">
诗圣杜甫,致君尧舜,笔底波澜写尽人间沧桑
</div>
<div style="font-size: 14px; color: #666; font-style: italic;
浏览器搜索"唐诗.中国"中文域名查看完整版
</div>
</header>
<div class="progress-bar" id="progressBar"></div>
<div id="mapLoader">
<div class="loader-spinner"></div>
<span>加载中…</span>
<div style="margin-top: 10px; font-size: 14px; color: #666;">正在准备杜甫的精彩人生旅程</div>
</div>
<div id="map"></div>
<!-- 信息卡片 -->
<div id="infoCard" class="info-card">
<img id="infoImage" class="info-image" src="" alt="" loading="lazy">
<div class="info-content">
<!-- 语言切换按钮 -->
<div class="lang-switch">
<button id="btnZh" class="lang-btn active">中文</button>
<button id="btnEn" class="lang-btn">English</button>
</div>
<h3 id="infoTitle" class="info-title"></h3>
<div id="infoYear" class="info-year"></div>
<div id="infoDescription" class="info-description"></div>
<div id="infoPoem" class="info-poem"></div>
<div id="infoAnecdote" class="info-description"></div>
</div>
<button class="info-close" onclick="hideInfoCard()">×</button>
<div class="scroll-control">
<button id="pauseScrollBtn" class="scroll-btn" onclick="toggleScroll()">❚❚</button>
</div>
<!-- 折叠按钮 -->
<button class="collapse-btn" onclick="toggleCardCollapse()"></button>
</div>
<!-- 控制按钮 -->
<div class="controls">
<button class="control-btn" id="playBtn" onclick="toggleAnimation()" data-tooltip="自动播放/暂停">▶</button>
<button class="control-btn" id="prevBtn" onclick="prevPoint()" data-tooltip="上一个地点">◀</button>
<button class="control-btn" id="nextBtn" onclick="nextPoint()" data-tooltip="下一个地点">▶</button>
</div>
<!-- 回到顶部按钮 -->
<div class="back-to-top" id="backToTop" onclick="scrollToTop()">
<i class="fas fa-arrow-up"></i>
</div>
<!-- 阶段颜色指示器 -->
<div class="stage-indicator">
<div class="stage-title">人生阶段</div>
<div class="stage-colors" id="stageColors"></div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-ant-path@1.3.0/dist/leaflet-ant-path.js"></script>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<script>
// 全局变量定义
let map;
let allPoints = [];
let currentPointIndex = 0;
let markers = [];
let pathLines = [];
let horseMarker = null;
let isAnimating = false;
let animationInterval = null;
let isEn = false;
let isScrolling = true;
let scrollAnimationId = null;
let lastScrollY = 0;
const scrollSpeed = 0.5; // 滚动速度(像素/毫秒)- 减慢为原来的一半
const animationDelay = 20000; // 动画延迟(毫秒)- 延长至20秒,让用户有更多时间欣赏内容
// 定义杜甫人生阶段名称和对应的古典风颜色
const stageNames = [
{ stage: 1, name: "出生与童年", color: "#8B4513" }, // 棕色
{ stage: 2, name: "漫游求仕", color: "#A0522D" }, // 红棕色
{ stage: 3, name: "困居长安", color: "#CD853F" }, // 秘鲁色
{ stage: 4, name: "安史之乱", color: "#DEB887" }, // 原木色
{ stage: 5, name: "蜀中漂泊", color: "#BC8F8F" }, // 玫瑰棕色
{ stage: 6, name: "夔州流寓", color: "#D2B48C" }, // 棕褐色
{ stage: 7, name: "荆湘漂泊", color: "#F5DEB3" }, // 小麦色
{ stage: 8, name: "舟中长逝", color: "#7D4F24" } // 深棕色
];
const stageNamesEn = [
{ stage: 1, name: "Birth & Childhood", color: "#8B4513" },
{ stage: 2, name: "Wandering & Seeking Office", color: "#A0522D" },
{ stage: 3, name: "Struggling in Chang'an", color: "#CD853F" },
{ stage: 4, name: "An Lushan Rebellion", color: "#DEB887" },
{ stage: 5, name: "Wandering in Shu", color: "#BC8F8F" },
{ stage: 6, name: "Exile in Kuizhou", color: "#D2B48C" },
{ stage: 7, name: "Wandering in Jing & Xiang", color: "#F5DEB3" },
{ stage: 8, name: "Final Years on Boat", color: "#7D4F24" }
];
/* 杜甫足迹数据 */
allPoints = [
{
"name": "巩县",
"nameEn": "Gongxian County",
"year": 712,
"stage": 1,
"coord": [34.7, 112.9],
"event": "出生于河南巩县(今河南巩义市)",
"eventEn": "Born in Gongxian County, Henan (now Gongyi City, Henan)",
"poem": "七龄思即壮,开口咏凤凰",
"poemEn": "At seven my thoughts turned bold, and I opened my mouth to sing of the phoenix",
"image": "./images/gongxian.jpg",
"fallbackImage": "https://picsum.photos/id/1015/800/600",
"culture": {
"lines": ["七龄思即壮,开口咏凤凰", "九龄书大字,有作成一囊"],
"linesEn": ["At seven my thoughts turned bold, and I opened my mouth to sing of the phoenix", "At nine I wrote large characters, my works filling a satchel"],
"anecdote": "杜甫出生于官宦世家,祖父杜审言是初唐著名诗人。杜甫晚年在《壮游》诗中自述'七龄思即壮,开口咏凤凰'。巩县是杜甫的出生地,这里的山水风光和家族文化对他的诗歌创作产生了深远影响。",
"anecdoteEn": "Du Fu was born into an official family. His grandfather Du Shenyan was a famous poet in the early Tang Dynasty. In his poem 'Grand Tour', Du Fu recalled that at the age of seven his thoughts turned bold and he sang of the phoenix. Gongxian was Du Fu's birthplace, and the landscape and family culture here had a profound impact on his poetic creation."
}
},
{
"name": "洛阳",
"nameEn": "Luoyang",
"year": 744,
"stage": 2,
"coord": [34.6, 112.4],
"event": "在洛阳与李白相识,同游梁宋",
"eventEn": "Met Li Bai in Luoyang and traveled together in the Liang-Song region",
"poem": "二年客东都,所历厌机巧",
"poemEn": "Two years a sojourner in the Eastern Capital, weary of all its worldly scheming",
"image": "./images/luoyang.jpg",
"fallbackImage": "https://picsum.photos/id/1036/800/600",
"culture": {
"lines": ["二年客东都,所历厌机巧", "李侯金闺彦,脱身事幽讨"],
"linesEn": ["Two years a sojourner in the Eastern Capital, weary of all its worldly scheming", "Li Bai, talent of the golden court, broke free to seek the secluded way"],
"anecdote": "天宝三载(744年),杜甫在洛阳与李白、高适等诗人相识相交,结下深厚友谊。他们一同漫游梁、宋(今河南开封、商丘一带),登吹台、怀禹庙,饮酒赋诗,谈古论今。次年两人又相约同游东鲁,有'醉眠秋共被,携手日同行'之谊。这段经历不仅丰富了杜甫的人生阅历,也为他后来的诗歌创作积累了素材。",
"anecdoteEn": "In 744, Du Fu met Li Bai in Luoyang, just dismissed from court with gold, and became acquainted with Gao Shi, forming deep friendships. They roamed together in Liang and Song (now Kaifeng and Shangqiu areas in Henan), drinking wine, composing poetry, and discussing ancient and modern affairs. This experience not only enriched Du Fu's life experience but also accumulated material for his later poetic creation."
}
},
{
"name": "泰山",
"nameEn": "Mount Tai",
"year": 736,
"stage": 2,
"coord": [36.2, 117.1],
"event": "北游齐赵,登泰山,作《望岳》",
"eventEn": "Traveled north to Qi and Zhao, climbed Mount Tai, composing 'Viewing the Summit'",
"poem": "会当凌绝顶,一览众山小",
"poemEn": "I must climb to the very peak, to see all mountains as small",
"image": "./images/taishan.jpg",
"fallbackImage": "https://picsum.photos/id/1039/800/600",
"culture": {
"lines": ["岱宗夫如何?齐鲁青未了", "造化钟神秀,阴阳割昏晓", "会当凌绝顶,一览众山小"],
"linesEn": ["What's Mount Tai like? Green continues across Qi and Lu", "Nature converges its divine beauty; yin and yang divide dawn and dusk", "I must climb to the very peak, to see all mountains as small"],
"anecdote": "25岁的杜甫北游齐赵(今山东、河北一带),登上东岳泰山,写下了千古名篇《望岳》。诗中'会当凌绝顶,一览众山小'一句,表达了诗人青年时期的壮志豪情和积极进取的精神。这首诗被后人誉为'咏泰山的绝唱',也奠定了杜甫在诗坛的地位。",
"anecdoteEn": "At the age of 25, Du Fu traveled north to Qi and Zhao (now Shandong and Hebei areas), climbed Mount Tai, the eastern sacred mountain, and wrote the timeless masterpiece 'Viewing the Summit'. The line 'I must climb to the very peak, to see all mountains as small' expresses the poet's ambition and enterprising spirit in his youth. This poem is praised by later generations as 'the best poem on Mount Tai' and also established Du Fu's position in the poetry world."
}
},
{
"name": "长安",
"nameEn": "Chang'an",
"year": 746,
"stage": 3,
"coord": [34.3, 108.8],
"event": "初到长安求仕,开始十年困居生涯",
"eventEn": "First arrival in Chang'an seeking official position, beginning ten years of struggling life",
"poem": "朱门酒肉臭,路有冻死骨",
"poemEn": "In vermilion gates, wine and meat rot; on the road, frozen bones lie",
"image": "./images/changan.jpg",
"fallbackImage": "https://picsum.photos/id/1043/800/600",
"culture": {
"lines": ["朱门酒肉臭,路有冻死骨", "荣枯咫尺异,惆怅难再述"],
"linesEn": ["In vermilion gates, wine and meat rot; on the road, frozen bones lie", "Prosperity and decline differ within inches; sorrow is hard to recount"],
"anecdote": "杜甫怀着'致君尧舜上,再使风俗淳'的政治理想来到长安,但仕途不顺,一直未能得到重用。在长安的十年间,他目睹了上层社会的奢侈腐化和底层人民的苦难生活,思想发生了深刻变化,诗歌创作也从早期的浪漫主义转向现实主义,写下了许多反映社会现实的优秀诗篇。",
"anecdoteEn": "Du Fu came to Chang'an with the political ideal of 'assisting the emperor to be like Yao and Shun, and making the customs pure again', but his official career was not smooth, and he was never重用. During his ten years in Chang'an, he witnessed the luxury and corruption of the upper class and the苦难 of the底层 people, his thoughts underwent profound changes, and his poetic creation also shifted from early romanticism to realism, writing many excellent poems reflecting social reality."
}
},
{
"name": "奉先",
"nameEn": "Fengxian",
"year": 755,
"stage": 3,
"coord": [35.0, 109.3],
"event": "回家省亲,途经骊山,作《自京赴奉先县咏怀五百字》",
"eventEn": "Returned home to visit relatives, passing through Mount Li, composing 'Five Hundred Words of Chanting Thoughts on the Journey from the Capital to Fengxian County'",
"poem": "入门闻号啕,幼子饥已卒",
"poemEn": "Entering the door, I heard wailing; my youngest son had died of hunger",
"image": "./images/lishan.jpg",
"fallbackImage": "https://picsum.photos/id/1057/800/600",
"culture": {
"lines": ["入门闻号啕,幼子饥已卒", "所愧为人父,无食致夭折"],
"linesEn": ["Entering the door, I heard wailing; my youngest son had died of hunger", "Ashamed to be a father, having no food leading to early death"],
"anecdote": "杜甫从长安回家省亲,途经骊山,看到唐玄宗和杨贵妃在华清宫寻欢作乐,而沿途百姓却处于水深火热之中。到家后,他发现小儿子已经饿死。这一系列的遭遇,使他写出了长诗《自京赴奉先县咏怀五百字》,其中'朱门酒肉臭,路有冻死骨'一句,成为千古名句,深刻揭露了封建社会的阶级矛盾。",
"anecdoteEn": "Du Fu returned home from Chang'an to visit relatives, passing through Mount Li, where he saw Emperor Xuanzong and Consort Yang enjoying themselves in Huaqing Palace, while the people along the way were suffering deeply. When he arrived home, he found that his youngest son had starved to death. These experiences inspired him to write the long poem 'Five Hundred Words of Chanting Thoughts on the Journey from the Capital to Fengxian County', in which the line 'In vermilion gates, wine and meat rot; on the road, frozen bones lie' became an immortal quote, profoundly exposing the class contradictions in feudal society."
}
},
{
"name": "灵武",
"nameEn": "Lingwu",
"year": 756,
"stage": 4,
"coord": [37.9, 106.3],
"event": "乱中被俘困居长安,后逃归凤翔行在",
"eventEn": "Captured and detained in fallen Chang'an, later fled to the provisional court at Fengxiang",
"poem": "国破山河在,城春草木深",
"poemEn": "The country shattered, mountains and rivers remain; the city in spring, grass and trees grow thick",
"image": "./images/fenjie.jpg",
"fallbackImage": "https://picsum.photos/id/1067/800/600",
"culture": {
"lines": ["国破山河在,城春草木深", "感时花溅泪,恨别鸟惊心", "烽火连三月,家书抵万金"],
"linesEn": ["The country shattered, mountains and rivers remain; the city in spring, grass and trees grow thick", "Saddened by the times, flowers shed tears; hating separation, birds alarm the heart", "Beacon fires have lasted three months; a letter from home is worth ten thousand gold"],
"anecdote": "755年冬,安史之乱爆发;756年六月潼关失守,唐玄宗奔蜀,长安沦陷。杜甫听到唐肃宗在灵武即位的消息后,安顿家小于鄜州,只身北上投奔,途中被叛军俘获,押回长安。757年春,他在沦陷的长安写下《春望》,感时伤乱。同年四月,他冒死逃出长安,穿越两军对峙的战场,抵达凤翔行在,被唐肃宗授为左拾遗。",
"anecdoteEn": "In the winter of 755, the An Lushan Rebellion broke out; in June 756, Tongguan fell, Emperor Xuanzong fled to Shu, and Chang'an was occupied. Hearing that Emperor Suzong had ascended the throne in Lingwu, Du Fu settled his family in Fuzhou and headed north alone, but was captured by rebel forces and taken back to Chang'an. In the spring of 757, he wrote 'Spring View' in the occupied capital. In April, he escaped and crossed the battle lines to reach the emperor's provisional court at Fengxiang, where he was appointed Left Remonstrant by Emperor Suzong."
}
},
{
"name": "华州",
"nameEn": "Huazhou",
"year": 758,
"stage": 4,
"coord": [34.5, 109.7],
"event": "因疏救房琯被贬华州司功参军,作'三吏'、'三别'",
"eventEn": "Demoted to Huazhou for defending Fang Guan, composing 'Three Official Poems' and 'Three Farewell Poems'",
"poem": "暮投石壕村,有吏夜捉人",
"poemEn": "At dusk, I lodge in Shihao Village; officials come at night to seize people",
"image": "./images/huazhou.jpg",
"fallbackImage": "https://picsum.photos/id/1081/800/600",
"culture": {
"lines": ["暮投石壕村,有吏夜捉人", "老翁逾墙走,老妇出门看"],
"linesEn": ["At dusk, I lodge in Shihao Village; officials come at night to seize people", "The old man climbs over the wall to flee; the old woman goes out to see"],
"anecdote": "杜甫因上书疏救房琯,触怒唐肃宗,被贬为华州司功参军。在华州期间,他目睹了战乱给人民带来的深重灾难,写下了著名的'三吏'(《新安吏》、《石壕吏》、《潼关吏》)和'三别'(《新婚别》、《垂老别》、《无家别》)。这些诗篇真实地反映了安史之乱中劳动人民的苦难生活,具有深刻的人民性和强烈的现实主义精神,被称为'诗史'。",
"anecdoteEn": "Du Fu was demoted to Huazhou for submitting a memorial to defend Fang Guan, which angered Emperor Suzong. During his time in Huazhou, he witnessed the深重灾难 brought to the people by the war, and wrote the famous 'Three Official Poems' (Xin'an Official, Shihao Official, Tongguan Official) and 'Three Farewell Poems' (Newlywed Farewell, Old Age Farewell, Homeless Farewell). These poems truthfully reflect the suffering of working people during the An Lushan Rebellion, with profound people's character and strong realistic spirit, and are called 'historical poetry'."
}
},
{
"name": "成都",
"nameEn": "Chengdu",
"year": 759,
"stage": 5,
"coord": [30.6, 104.0],
"event": "弃官入蜀,在成都西郊建草堂定居",
"eventEn": "Abandoned official position and entered Shu, building Thatched Cottage in western suburbs of Chengdu to settle",
"poem": "安得广厦千万间,大庇天下寒士俱欢颜",
"poemEn": "How I wish I could get ten thousand spacious houses, to shelter all the poor scholars under heaven with happy faces",
"image": "./images/chengdu.jpg",
"fallbackImage": "https://picsum.photos/id/1036/800/600",
"culture": {
"lines": ["安得广厦千万间,大庇天下寒士俱欢颜", "风雨不动安如山。呜呼!何时眼前突兀见此屋,吾庐独破受冻死亦足"],
"linesEn": ["How I wish I could get ten thousand spacious houses, to shelter all the poor scholars under heaven with happy faces", "Safe and secure like mountains amid wind and rain. Alas! When will such houses suddenly appear before my eyes? Even if my own cottage is destroyed and I freeze to death, I would be satisfied"],
"anecdote": "759年,杜甫因战乱和饥荒,放弃官职,携家带口入蜀避难,最终在成都西郊浣花溪畔建了一座草堂定居下来。在成都的四年间,他得到了严武等朋友的帮助,生活相对安定,创作了大量优秀诗篇,如《茅屋为秋风所破歌》、《春夜喜雨》等。成都草堂后来成为纪念杜甫的重要场所,被誉为'诗圣故居'。",
"anecdoteEn": "In 759, due to war and famine, Du Fu abandoned his official position, took his family to Shu region to take refuge, and finally built a thatched cottage by Huanhua Stream in the western suburbs of Chengdu to settle down. During his four years in Chengdu, he received help from friends like Yan Wu, lived a relatively stable life, and created many excellent poems, such as 'Song of the Thatched Cottage Broken by Autumn Wind' and 'Happy Rain on a Spring Night'. Chengdu Thatched Cottage later became an important place to commemorate Du Fu, known as 'Former Residence of the Poet Saint'."
}
},
{
"name": "梓州",
"nameEn": "Zizhou",
"year": 762,
"stage": 5,
"coord": [31.4, 105.6],
"event": "送严武还朝,遭徐知道之乱,流寓梓州",
"eventEn": "After Yan Wu was recalled to court, blocked by the Xu Zhidao rebellion, sojourned in Zizhou",
"poem": "剑外忽传收蓟北,初闻涕泪满衣裳",
"poemEn": "News from beyond Sword Gate: Jibei recovered; at first hearing, tears soak my clothes",
"image": "./images/jiangxinsi.jpg",
"fallbackImage": "https://picsum.photos/id/1045/800/600",
"culture": {
"lines": ["剑外忽传收蓟北,初闻涕泪满衣裳", "却看妻子愁何在,漫卷诗书喜欲狂"],
"linesEn": ["News from beyond Sword Gate: Jibei recovered; at first hearing, tears soak my clothes", "I look at my wife and children — where is their sorrow? I roll up my books, wild with joy"],
"anecdote": "762年七月,玄宗、肃宗相继去世,代宗即位,召严武还朝。杜甫送严武至绵州,恰逢剑南兵马使徐知道叛乱,归路断绝,只得流寓梓州、阆州一带。763年春,他在梓州听闻安史之乱平定的消息,欣喜若狂,写下了'生平第一快诗'《闻官军收河南河北》,诗中'剑外忽传收蓟北,初闻涕泪满衣裳'一句,表达了他听到平叛胜利消息时的激动心情。",
"anecdoteEn": "In 762, Yan Wu was recalled to court; Du Fu escorted him to Mianzhou, but the rebellion of Xu Zhidao blocked his way back to Chengdu, forcing him to sojourn in Zizhou and Langzhou. In the spring of 763, hearing that the An Lushan Rebellion had ended, he wrote his 'happiest poem of a lifetime', 'Hearing That Government Troops Recovered Henan and Hebei'. The line 'Suddenly came news from beyond Jianmen that Jibei was recovered; on first hearing, tears filled my clothes' expresses his excitement when hearing the news of the victory in suppressing the rebellion."
}
},
{
"name": "夔州",
"nameEn": "Kuizhou",
"year": 766,
"stage": 6,
"coord": [31.0, 109.6],
"event": "到达夔州(今重庆奉节),开始近两年的寓居生活",
"eventEn": "Arrived in Kuizhou (now Fengjie, Chongqing), beginning nearly two years of exile life",
"poem": "无边落木萧萧下,不尽长江滚滚来",
"poemEn": "Boundless falling leaves rustle down; endless Yangtze River rolls on",
"image": "./images/kuizhou.jpg",
"fallbackImage": "https://picsum.photos/id/1085/800/600",
"culture": {
"lines": ["风急天高猿啸哀,渚清沙白鸟飞回", "无边落木萧萧下,不尽长江滚滚来", "万里悲秋常作客,百年多病独登台"],
"linesEn": ["Wind urgent, sky high, gibbons wail mournfully; islet clear, sand white, birds fly back", "Boundless falling leaves rustle down; endless Yangtze River rolls on", "Ten thousand miles of sad autumn, always a traveler; hundred years of many illnesses, alone climbing the terrace"],
"anecdote": "766年,杜甫到达夔州,在这里度过了近两年相对稳定的寓居生活。期间,他得到了柏茂琳的帮助,经管一些公田,生活有所改善。在夔州期间,杜甫的诗歌创作达到了新的高峰,写下了《登高》、《秋兴八首》、《阁夜》等大量优秀诗篇。这些诗篇不仅艺术成就很高,而且思想深刻,被后人誉为杜甫诗歌的巅峰之作。",
"anecdoteEn": "In 766, Du Fu arrived in Kuizhou, where he spent nearly two years of relatively stable exile life. During this period, he received help from Bai Maolin, managed some public fields, and his life improved somewhat. During his stay in Kuizhou, Du Fu's poetic creation reached a new peak, writing many excellent poems such as 'Climbing High', 'Eight Poems of Autumn Hues', and 'Night in the Pavilion'. These poems not only have high artistic achievements but also profound thoughts, and are praised by later generations as the pinnacle of Du Fu's poetry."
}
},
{
"name": "江陵",
"nameEn": "Jiangling",
"year": 768,
"stage": 7,
"coord": [30.3, 112.2],
"event": "离开夔州,顺江而下,到达江陵",
"eventEn": "Left Kuizhou, traveled downstream along the river, arrived in Jiangling",
"poem": "王郎酒酣拔剑斫地歌莫哀,我能拔尔抑塞磊落之奇才",
"poemEn": "Wang Lang, wine-flushed, draws his sword and strikes the earth, singing not of sorrow; I can release your pent-up, towering genius",
"image": "./images/jiangling.jpg",
"fallbackImage": "https://picsum.photos/id/1043/800/600",
"culture": {
"lines": ["王郎酒酣拔剑斫地歌莫哀", "我能拔尔抑塞磊落之奇才"],
"linesEn": ["Wang Lang, wine-flushed, draws his sword and strikes the earth, singing not of sorrow", "I can release your pent-up, towering genius"],
"anecdote": "768年,杜甫离开夔州,打算返回河南老家。他顺长江而下,先后来到江陵、公安、岳阳等地。在江陵,他与故旧交游,为怀才不遇的友人王司直击节高歌,写下《短歌行·赠王郎司直》,抒发自己晚年壮志难酬的悲慨。由于年老体衰、时局动荡,他最终未能实现返回河南老家的愿望,只得继续漂泊南下。",
"anecdoteEn": "In 768, Du Fu left Kuizhou, intending to return to his hometown in Henan. He traveled downstream along the Yangtze River, coming to Jiangling, Gongan, Yueyang and other places successively. In Jiangling, he befriended old acquaintances and wrote 'A Short Song for Master Wang', singing high for a frustrated friend and voicing his own grief of unfulfilled ambitions in old age. Due to old age, poor health and the turbulent times, he never realized his wish to return home, and had to keep drifting south."
}
},
{
"name": "岳阳",
"nameEn": "Yueyang",
"year": 768,
"stage": 7,
"coord": [29.3, 113.1],
"event": "登岳阳楼,作《登岳阳楼》",
"eventEn": "Climbed Yueyang Tower, composing 'Climbing Yueyang Tower'",
"poem": "吴楚东南坼,乾坤日夜浮",
"poemEn": "Wu and Chu split in southeast; heaven and earth float day and night",
"image": "./images/yueyang.jpg",
"fallbackImage": "https://picsum.photos/id/1048/800/600",
"culture": {
"lines": ["昔闻洞庭水,今上岳阳楼", "吴楚东南坼,乾坤日夜浮", "亲朋无一字,老病有孤舟"],
"linesEn": ["I heard of Dongting Lake before, now I climb Yueyang Tower", "Wu and Chu split in southeast; heaven and earth float day and night", "No word from relatives and friends; old, ill, with a lonely boat"],
"anecdote": "768年冬,杜甫到达岳阳,登上了著名的岳阳楼,写下了《登岳阳楼》一诗。诗中'吴楚东南坼,乾坤日夜浮'一句,以雄奇的笔触描绘了洞庭湖的壮阔景象,被誉为'古今绝唱'。同时,诗中也流露出诗人晚年漂泊无依的凄凉处境和对国家动荡不安的忧虑之情。",
"anecdoteEn": "In the winter of 768, Du Fu arrived in Yueyang and climbed the famous Yueyang Tower, writing the poem 'Climbing Yueyang Tower'. The line 'Wu and Chu split in southeast; heaven and earth float day and night' depicts the magnificent scenery of Dongting Lake with bold strokes, praised as 'the best poem through the ages'. At the same time, the poem also reveals the poet's desolate situation of wandering without support in his later years and his worry about the country's turbulence."
}
},
{
"name": "潭州",
"nameEn": "Tanzhou",
"year": 770,
"stage": 8,
"coord": [28.2, 112.9],
"event": "漂泊至潭州(今湖南长沙),抱病在身",
"eventEn": "Wandered to Tanzhou (now Changsha, Hunan), ill in body",
"poem": "战血流依旧,军声动至今",
"poemEn": "War blood still flows; the sound of armies stirs to this day",
"image": "./images/hengyang.jpg",
"fallbackImage": "https://picsum.photos/id/1089/800/600",
"culture": {
"lines": ["战血流依旧,军声动至今"],
"linesEn": ["War blood still flows; the sound of armies stirs to this day"],
"anecdote": "770年,杜甫漂泊至潭州,此时他已经59岁,年老体衰,贫病交加。在潭州,他得到了当地官员和朋友的一些帮助,但病情日益加重。临终前,他在湘江舟中写下绝笔诗《风疾舟中伏枕书怀》,至死仍以'战血流依旧,军声动至今'挂念着战乱中的家国。这一年冬天,杜甫在从潭州前往岳阳的船上逝世,结束了他颠沛流离的一生。杜甫的诗歌深刻反映了唐朝由盛转衰的历史过程,被后人尊称为'诗史',他本人也被尊称为'诗圣'。",
"anecdoteEn": "In 770, Du Fu wandered to Tanzhou, at the age of 59, old, frail, poor and ill. In Tanzhou, he received some help from local officials and friends, but his condition deteriorated day by day. Before his death, he wrote his final poem 'Lying Ill on a Boat, Pouring Out My Heart' on the Xiang River, still worrying about the war-torn country. That winter, Du Fu passed away on a boat traveling from Tanzhou to Yueyang, ending his wandering life. Du Fu's poems profoundly reflect the historical process of the Tang Dynasty from prosperity to decline, and are respected by later generations as 'historical poetry', and he himself is also respected as 'Poet Saint'."
}
}
];
/* 初始化地图 */
function initMap() {
// 创建地图实例
map = L.map('map', {
preferCanvas: true,
zoomControl: false,
minZoom: 3,
maxZoom: 14
}).setView([34, 110], 5);
// 添加底图图层(使用高德地图服务)
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
subdomains: '1234',
crossOrigin: true,
reuseTiles: true,
maxZoom: 14
}).addTo(map);
// 添加缩放控件
L.control.zoom({position: 'bottomright'}).addTo(map);
// 绘制阶段颜色指示器
initStageIndicators();
// 绘制路线和点位
drawPathSegments();
createPointMarkers();
// 创建骑马标记
createHorseMarker();
// 隐藏加载器
setTimeout(() => {
document.getElementById('mapLoader').style.opacity = '0';
document.getElementById('mapLoader').style.pointerEvents = 'none';
setTimeout(() => {
document.getElementById('mapLoader').style.display = 'none';
}, 500);
}, 1000);
}
/* 绘制路线分段 */
function drawPathSegments() {
// 清除已有路线
pathLines.forEach(line => map.removeLayer(line));
pathLines = [];
// 绘制每段路线(直接连接相邻两点)
for (let i = 0; i < allPoints.length - 1; i++) {
const startPoint = allPoints[i];
const endPoint = allPoints[i + 1];
// 使用起点的阶段颜色
const stageInfo = stageNames.find(s => s.stage === startPoint.stage);
const color = stageInfo ? stageInfo.color : '#000000';
// 使用蚂蚁线绘制路线
const antPath = L.polyline.antPath(
[startPoint.coord, endPoint.coord],
{
"paused": false,
"reverse": false,
"delay": 1000,
"dashArray": [10, 20],
"weight": 3,
"color": color,
"pulseColor": '#ffffff',
"opacity": 0.7
}
).addTo(map);
pathLines.push(antPath);
}
}
/* 创建点位标记 */
function createPointMarkers() {
// 初始化加载进度
const progressBar = document.getElementById('progressBar');
allPoints.forEach((point, index) => {
// 更新加载进度
progressBar.style.width = `${(index / allPoints.length) * 100}%`;
// 获取阶段对应的颜色
const stageInfo = stageNames.find(s => s.stage === point.stage);
const color = stageInfo ? stageInfo.color : '#000000';
// 创建圆形标记
const marker = L.circleMarker(point.coord, {
radius: 10,
fillColor: color,
color: '#ffffff',
weight: 2,
opacity: 1,
fillOpacity: 0.8
}).addTo(map);
// 存储原始样式,用于后续加亮效果
marker.originalStyle = {
radius: 10,
fillColor: color,
color: '#ffffff',
weight: 2,
opacity: 1,
fillOpacity: 0.8
};
// 加亮样式 - 颜色加深、尺寸更大
// 生成深色版本的颜色
const darkenColor = (color) => {
// 简单的颜色加深方法:移除透明度并稍微加深颜色值
let hex = color.replace('#', '');
// 转换为RGB
let r = parseInt(hex.substring(0, 2), 16);
let g = parseInt(hex.substring(2, 4), 16);
let b = parseInt(hex.substring(4, 6), 16);
// 深色处理(减少亮度20%)
r = Math.max(0, Math.floor(r * 0.8));
g = Math.max(0, Math.floor(g * 0.8));
b = Math.max(0, Math.floor(b * 0.8));
// 转回十六进制
return '#' + [r, g, b].map(x => {
const hex = x.toString(16);
return hex.length === 1 ? '0' + hex : hex;
}).join('');
};
const darkColor = darkenColor(color);
marker.highlightStyle = {
radius: 18, // 更大的半径
fillColor: darkColor, // 使用加深后的颜色
color: '#ffd700', // 金色边框
weight: 4, // 更粗的边框
opacity: 1,
fillOpacity: 1,
className: 'highlight-marker' // 添加类名用于可能的CSS动画
};
// 添加点击事件
marker.on('click', () => {
currentPointIndex = index;
showPointInfo(point);
moveHorseToPoint(index);
});
// 添加悬停效果
marker.on('mouseover', () => {
marker.setStyle(marker.highlightStyle);
});
marker.on('mouseout', () => {
// 只有当不是当前选中的点时才恢复样式
if (index !== currentPointIndex) {
marker.setStyle(marker.originalStyle);
}
});
// 添加弹出信息 - 中英文并行显示
marker.bindPopup(`<div class="bilingual-text">
<div><b>${point.name}</b></div>
<div style="font-size: 14px;">${point.year}年 | ${point.event}</div>
<div><b>${point.nameEn}</b></div>
<div style="font-size: 12px;">${point.year} A.D. | ${point.eventEn}</div>
</div>`, {
className: 'custom-popup',
closeButton: true,
minWidth: 200
});
markers.push(marker);
});
}
/* 创建骑马标记 */
function createHorseMarker() {
// 创建自定义图标
const horseIcon = L.divIcon({
html: '🐎',
className: 'horse-marker',
iconSize: [30, 30],
iconAnchor: [15, 15]
});
// 创建标记
horseMarker = L.marker(allPoints[0].coord, {
icon: horseIcon,
zIndexOffset: 1000
}).addTo(map);
// 添加轻微的上下浮动动画
const markerElement = horseMarker.getElement();
markerElement.style.animation = 'horseFloat 3s ease-in-out infinite';
}
/* 移动骑马标记到指定点位 */
function moveHorseToPoint(index) {
const point = allPoints[index];
// 平滑移动效果
if (horseMarker) {
// 获取当前位置和目标位置
const currentLatLng = horseMarker.getLatLng();
const targetLatLng = L.latLng(point.coord);
// 创建路径线段数组,使马能够沿着路径移动
const pathPoints = [];
const numSteps = 50; // 路径上的点数量,越多路径越平滑
// 计算两点之间的路径点
for (let i = 0; i <= numSteps; i++) {
const progress = i / numSteps;
// 增加一些随机性,使路径看起来更自然
const randomFactor = 0.1 * (Math.sin(progress * Math.PI * 4) + Math.random() * 0.5);
const easedProgress = easeInOutCubic(progress);
// 计算中间位置,添加一些曲线效果
const lat = currentLatLng.lat +
(targetLatLng.lat - currentLatLng.lat) * easedProgress +
randomFactor * 0.1;
const lng = currentLatLng.lng +
(targetLatLng.lng - currentLatLng.lng) * easedProgress +
randomFactor * 0.1;
pathPoints.push({ lat, lng, progress });
}
// 创建马蹄印动画效果
createHorseTracks(currentLatLng, targetLatLng, numSteps);
// 移动到新位置 - 平滑过渡
const duration = 3000; // 动画持续时间,增加到3秒使移动更自然
const startTime = performance.now();
function moveHorse(timestamp) {
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / duration, 1);
// 使用缓动函数使动画更自然
const easedProgress = easeInOutCubic(progress);
// 找到当前进度对应的路径点
let currentPathPoint;
for (let i = 0; i < pathPoints.length; i++) {
if (pathPoints[i].progress >= easedProgress) {
currentPathPoint = pathPoints[i];
break;
}
}
if (currentPathPoint) {
// 更新骑马标记的位置
horseMarker.setLatLng(L.latLng(currentPathPoint.lat, currentPathPoint.lng));
if (progress < 1) {
requestAnimationFrame(moveHorse);
} else {
// 动画完成后更新点位状态
updateMarkerStates();
}
}
}
// 开始动画
requestAnimationFrame(moveHorse);
}
}
/* 创建马蹄印效果 */
function createHorseTracks(startPoint, endPoint, numSteps) {
// 计算每步之间的距离
const latStep = (endPoint.lat - startPoint.lat) / numSteps;
const lngStep = (endPoint.lng - startPoint.lng) / numSteps;
// 创建马蹄印标记
for (let i = 0; i < numSteps; i += 5) { // 每隔5步创建一个马蹄印
const lat = startPoint.lat + latStep * i;
const lng = startPoint.lng + lngStep * i;
// 随机偏移,使马蹄印看起来更自然
const randomLat = lat + (Math.random() - 0.5) * 0.01;
const randomLng = lng + (Math.random() - 0.5) * 0.01;
// 创建临时的马蹄印标记
const trackIcon = L.divIcon({
html: '👣',
className: 'horse-track',
iconSize: [30, 30],
iconAnchor: [15, 15]
});
const trackMarker = L.marker([randomLat, randomLng], {
icon: trackIcon,
zIndexOffset: 500
}).addTo(map);
// 设置淡出动画
setTimeout(() => {
let opacity = 1;
const fadeInterval = setInterval(() => {
opacity -= 0.1;
trackMarker.setOpacity(opacity);
if (opacity <= 0) {
clearInterval(fadeInterval);
map.removeLayer(trackMarker);
}
}, 100);
}, 1000);
}
}
/* 缓动函数 - 使动画更自然 */
function easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
}
/* 显示点位信息 */
function showPointInfo(point) {
const card = document.getElementById('infoCard');
const title = document.getElementById('infoTitle');
const year = document.getElementById('infoYear');
const description = document.getElementById('infoDescription');
const poem = document.getElementById('infoPoem');
const anecdote = document.getElementById('infoAnecdote');
const image = document.getElementById('infoImage');
// 设置内容 - 中英文并行显示
title.innerHTML = `<div class="bilingual-text">
<div class="chinese-text">${point.name}</div>
<div class="english-text">${point.nameEn}</div>
</div>`;
year.innerHTML = `<div class="bilingual-text">
<div class="chinese-text">${point.year}年 | ${point.event}</div>
<div class="english-text">${point.year} A.D. | ${point.eventEn}</div>
</div>`;
// 检查是否有文化信息
if (point.culture && point.culture.lines) {
// 显示诗句题目和内容,中英文并行显示
const poemTitle = point.culture.title || '诗句'; // 默认题目
const poemTitleEn = point.culture.titleEn || 'Verse'; // 默认英文题目
const poemHtml = `
<div class="bilingual-text poem-title">
<div class="chinese-text">${poemTitle}</div>
<div class="english-text">${poemTitleEn}</div>
</div>
<div class="bilingual-text">
<div class="chinese-text poem-highlight">${point.culture.lines.join('<br>')}</div>
<div class="english-text">${point.culture.linesEn.join('<br>')}</div>
</div>`;
poem.innerHTML = poemHtml;
// 显示文化轶事,中英文并行显示
if (point.culture.anecdote) {
anecdote.innerHTML = `<div class="bilingual-text">
<div class="chinese-text">${point.culture.anecdote}</div>
<div class="english-text">${point.culture.anecdoteEn}</div>
</div>`;
anecdote.style.display = 'block';
} else {
anecdote.style.display = 'none';
}
} else {
poem.innerHTML = `<div class="bilingual-text">
<div class="chinese-text poem-highlight">${point.poem}</div>
<div class="english-text">${point.poemEn}</div>
</div>`;
anecdote.style.display = 'none';
}
// 设置图片
image.onload = function() {
image.classList.remove('image-loading');
};
image.onerror = function() {
// 如果加载失败,使用备用图片
image.src = point.fallbackImage || 'https://picsum.photos/id/1000/800/600';
image.classList.remove('image-loading');
};
// 开始加载图片
image.classList.add('image-loading');
image.src = point.image;
// 显示卡片
setTimeout(() => {
card.classList.add('active');
}, 100);
// 滚动到顶部
card.querySelector('.info-content').scrollTop = 0;
// 启动自动滚动
startAutoScroll();
}
/* 初始化阶段颜色指示器 - 中英文并行显示 */
function initStageIndicators() {
const container = document.getElementById('stageColors');
container.innerHTML = '';
// 同时显示中文和英文的阶段名称
stageNames.forEach((stage, index) => {
const item = document.createElement('div');
item.className = 'stage-item';
const colorBox = document.createElement('div');
colorBox.className = 'stage-color';
colorBox.style.backgroundColor = stage.color;
const nameContainer = document.createElement('div');
nameContainer.className = 'bilingual-text';
const nameZhSpan = document.createElement('div');
nameZhSpan.className = 'chinese-text';
nameZhSpan.textContent = stage.name;
const nameEnSpan = document.createElement('div');
nameEnSpan.className = 'english-text';
nameEnSpan.textContent = stageNamesEn[index]?.name || '';
nameContainer.appendChild(nameZhSpan);
nameContainer.appendChild(nameEnSpan);
item.appendChild(colorBox);
item.appendChild(nameContainer);
container.appendChild(item);
});
}
/* 更新标记状态 */
function updateMarkerStates() {
markers.forEach((marker, index) => {
if (index === currentPointIndex) {
// 当前点使用高亮样式
marker.setStyle(marker.highlightStyle);
marker.openPopup(); // 打开当前点的弹出信息
} else {
// 其他点使用原始样式
marker.setStyle(marker.originalStyle);
marker.closePopup(); // 关闭其他点的弹出信息
}
});
}
/* 显示前一个点位 */
function prevPoint() {
currentPointIndex = (currentPointIndex - 1 + allPoints.length) % allPoints.length;
const point = allPoints[currentPointIndex];
showPointInfo(point);
moveHorseToPoint(currentPointIndex);
}
/* 显示下一个点位 */
function nextPoint() {
currentPointIndex = (currentPointIndex + 1) % allPoints.length;
const point = allPoints[currentPointIndex];
showPointInfo(point);
moveHorseToPoint(currentPointIndex);
}
/* 切换自动播放 */
function toggleAnimation() {
const playBtn = document.getElementById('playBtn');
if (isAnimating) {
// 停止动画
clearInterval(animationInterval);
playBtn.textContent = '▶';
playBtn.setAttribute('data-tooltip', '自动播放');
} else {
// 开始动画
animationInterval = setInterval(nextPoint, animationDelay);
playBtn.textContent = '⏸';
playBtn.setAttribute('data-tooltip', '暂停自动播放');
}
isAnimating = !isAnimating;
}
/* 隐藏信息卡片 */
function hideInfoCard() {
const card = document.getElementById('infoCard');
card.classList.remove('active');
// 停止自动滚动
stopAutoScroll();
}
/* 切换卡片折叠状态 */
function toggleCardCollapse() {
const card = document.getElementById('infoCard');
card.classList.toggle('collapsed');
}
/* 开始自动滚动 */
function startAutoScroll() {
if (!isScrolling) return;
const content = document.querySelector('.info-content');
function scrollContent() {
if (!isScrolling) return;
// 检查是否到达底部
if (content.scrollTop + content.clientHeight >= content.scrollHeight) {
// 回到顶部
content.scrollTop = 0;
} else {
// 继续滚动
content.scrollTop += scrollSpeed;
}
scrollAnimationId = requestAnimationFrame(scrollContent);
}
// 先清除之前的动画
stopAutoScroll();
// 开始新的动画
scrollAnimationId = requestAnimationFrame(scrollContent);
}
/* 停止自动滚动 */
function stopAutoScroll() {
if (scrollAnimationId) {
cancelAnimationFrame(scrollAnimationId);
scrollAnimationId = null;
}
}
/* 切换自动滚动状态 */
function toggleScroll() {
const pauseBtn = document.getElementById('pauseScrollBtn');
if (isScrolling) {
// 暂停滚动
isScrolling = false;
pauseBtn.textContent = '▶';
stopAutoScroll();
} else {
// 继续滚动
isScrolling = true;
pauseBtn.textContent = '⏸';
startAutoScroll();
}
}
/* 中英文并行显示后,不再需要语言切换功能 */
function toggleLanguage() {
// 函数保留但不执行任何操作
console.log('中英文已并行显示,无需切换语言');
}
/* 回到顶部 */
function scrollToTop() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}
/* 处理滚动事件 */
function handleScroll() {
const backToTopBtn = document.getElementById('backToTop');
const currentScrollY = window.scrollY;
// 显示/隐藏回到顶部按钮
if (currentScrollY > 300) {
backToTopBtn.classList.add('visible');
} else {
backToTopBtn.classList.remove('visible');
}
// 保存滚动位置
lastScrollY = currentScrollY;
}
/* 页面加载完成后执行 */
window.addEventListener('load', function() {
// 初始化地图
initMap();
// 显示第一个点的信息
if (allPoints.length > 0) {
setTimeout(() => {
showPointInfo(allPoints[currentPointIndex]);
}, 1500);
}
// 添加滚动事件监听
window.addEventListener('scroll', handleScroll);
// 初始检查滚动位置
handleScroll();
// 添加信息卡片点击事件,展开/折叠卡片
const infoCard = document.getElementById('infoCard');
infoCard.addEventListener('click', function(e) {
// 如果点击的不是关闭按钮或折叠按钮,不执行操作
if (e.target === document.querySelector('.info-close') || e.target === document.querySelector('.collapse-btn')) {
return;
}
// 如果卡片已折叠,则展开
if (infoCard.classList.contains('collapsed')) {
infoCard.classList.remove('collapsed');
}
});
// 点击地图区域隐藏信息卡片(可选功能)
map.on('click', function(e) {
// 检查点击是否在信息卡片外
const cardRect = infoCard.getBoundingClientRect();
const clickX = e.originalEvent.clientX;
const clickY = e.originalEvent.clientY;
if (clickX < cardRect.left || clickX > cardRect.right ||
clickY < cardRect.top || clickY > cardRect.bottom) {
// 点击在卡片外,保持卡片状态不变
}
});
// 防止信息卡片中的链接点击事件冒泡到地图
infoCard.addEventListener('click', function(e) {
e.stopPropagation();
});
// 添加键盘事件监听
document.addEventListener('keydown', function(e) {
if (e.key === 'ArrowLeft') {
prevPoint();
} else if (e.key === 'ArrowRight') {
nextPoint();
} else if (e.key === ' ') {
e.preventDefault(); // 防止空格键滚动页面
toggleAnimation();
}
});
});
</script>
</body>
</html>
与其仰望诗人的背影,不如亲自“走”一遍他们的路——宋词.中国「行迹漫游」让你化身马上行人,沿李白、杜甫、苏轼等57位诗人的真实迁徙路线策马而行:每过一站,触发一篇该地系年诗作;入夜之后,更有灯笼满城的诗意夜景。长安的城垣、夔门的江涛、黄州的赤壁,皆非示意图,而是诗人真实踏过的山河。行迹数据依诗人年谱与正史传记交叉考订,每站标注年号纪年与系年诗作,寓学于游。
