<!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;
}
/* 折叠按钮样式 */
.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);
}
/* 高亮马图标轨迹样式 */
.highlight-horse-track {
font-size: 30px !important;
text-shadow: 0 0 15px rgba(255, 215, 0, 0.8), 0 0 30px rgba(255, 215, 0, 0.5);
transform-origin: center center;
animation: horsePulse 1.5s ease-in-out infinite, horseFloat 3s ease-in-out infinite;
filter: brightness(1.2);
}
@keyframes horsePulse {
0% {
transform: scale(0.8);
opacity: 0.7;
}
50% {
transform: scale(1.1);
opacity: 1;
}
100% {
transform: scale(0.8);
opacity: 0.7;
}
}
.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;
}
/* 诗题样式 */
.poem-title {
font-weight: bold;
font-size: 18px;
margin-bottom: 10px;
color: #8B4513;
text-align: center;
font-style: normal;
letter-spacing: 1px;
}
.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">
<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: "#009933" }, // 鲜艳绿色
{ stage: 2, name: "漫游求仕", color: "#00CC66" }, // 鲜亮绿色
{ stage: 3, name: "长安为官", color: "#FFCC00" }, // 明亮黄色
{ stage: 4, name: "半官半隐", color: "#3399FF" }, // 鲜亮蓝色
{ stage: 5, name: "安史之乱", color: "#FF6666" }, // 鲜艳红色
{ stage: 6, name: "辋川隐居", color: "#9966FF" }, // 紫色
{ stage: 7, name: "晚年皈依", color: "#FF9933" } // 橙色
];
const stageNamesEn = [
{ stage: 1, name: "Birth & Childhood", color: "#2E8B57" },
{ stage: 2, name: "Wandering & Seeking Office", color: "#3CB371" },
{ stage: 3, name: "Serving in Chang'an", color: "#90EE90" },
{ stage: 4, name: "Half Official, Half Hermit", color: "#008080" },
{ stage: 5, name: "An Lushan Rebellion", color: "#FF6666" },
{ stage: 6, name: "Retreat in Wangchuan", color: "#00CED1" },
{ stage: 7, name: "Later Years of Buddhist Devotion", color: "#800080" }
];
/* 王维足迹数据 */
allPoints = [
{
"name": "蒲州",
"nameEn": "Puzhou",
"year": 701,
"stage": 1,
"coord": [35.2, 110.9],
"event": "出生于蒲州(今山西永济市)",
"eventEn": "Born in Puzhou (now Yongji City, Shanxi)",
"poem": "空山不见人,但闻人语响",
"poemEn": "Empty mountain, no one to be seen; only the sound of human voices heard",
"image": "./images/puzhou.jpg",
"fallbackImage": "https://picsum.photos/id/1018/800/600",
"culture": {
"title": "鹿柴",
"titleEn": "Deer Enclosure",
"lines": ["空山不见人,但闻人语响", "返景入深林,复照青苔上"],
"linesEn": ["Empty mountain, no one to be seen; only the sound of human voices heard", "Afterglow enters the deep forest; again shines on the green moss"],
"anecdote": "王维出身于河东王氏,是唐代著名的世家大族。据《旧唐书》记载,王维九岁能文,精通音律,被誉为'神童'。蒲州位于黄河东岸,山清水秀,这里的自然风光对王维后来的山水诗创作产生了深远影响。《鹿柴》是王维山水诗的代表作之一,以简洁的语言描绘了山林的幽静。",
"anecdoteEn": "Wang Wei was born into the Wang family of Hedong, a famous aristocratic family in the Tang Dynasty. According to 'Old Book of Tang', Wang Wei could write prose at the age of nine and was proficient in music, known as a 'child prodigy'. Puzhou is located on the east bank of the Yellow River with beautiful mountains and clear waters, and the natural scenery here had a profound impact on Wang Wei's later landscape poetry creation. 'Deer Enclosure' is one of Wang Wei's representative landscape poems, depicting the tranquility of mountains and forests with concise language."
}
},
{
"name": "长安",
"nameEn": "Chang'an",
"year": 721,
"stage": 2,
"coord": [34.3, 108.8],
"event": "赴长安应试,与岐王李范、玉真公主结识",
"eventEn": "Traveled to Chang'an to take imperial examination, made acquaintance with Prince Qi Li Fan and Princess Yuzhen",
"poem": "红豆生南国,春来发几枝",
"poemEn": "Red beans grow in the southern land; how many branches bud in spring",
"image": "./images/changan.jpg",
"fallbackImage": "https://picsum.photos/id/1043/800/600",
"culture": {
"title": "相思",
"titleEn": "Yearning",
"lines": ["红豆生南国,春来发几枝", "愿君多采撷,此物最相思"],
"linesEn": ["Red beans grow in the southern land; how many branches bud in spring", "I hope you pick more; these are the best for expressing yearning"],
"anecdote": "青年王维离开家乡,前往长安求取功名。他凭借出众的才华和音乐天赋,得到岐王李范和玉真公主的赏识。王维曾在公主的宴席上演奏《郁轮袍》,其精湛的技艺和出众的文采令四座惊叹,为他后来的科举之路奠定了基础。《相思》是王维最著名的送别诗之一,以红豆象征相思之情,语言质朴而情感真挚。",
"anecdoteEn": "The young Wang Wei left his hometown and went to Chang'an to seek official career. With his outstanding talent and musical gifts, he gained the appreciation of Prince Qi Li Fan and Princess Yuzhen. Wang Wei once played 'Yu Lun Pao' at the princess's banquet, and his exquisite skills and outstanding literary talent amazed everyone, laying the foundation for his later imperial examination path. 'Yearning' is one of Wang Wei's most famous farewell poems, using red beans as a symbol of yearning, with simple language and sincere emotion."
}
},
{
"name": "渭城",
"nameEn": "Weicheng",
"year": 740,
"stage": 4,
"coord": [34.3, 108.5],
"event": "送友人元二出使安西都护府",
"eventEn": "Seeing off friend Yuan Er on a mission to Anxi Protectorate",
"poem": "劝君更尽一杯酒,西出阳关无故人",
"poemEn": "I urge you to finish one more cup of wine; beyond Yangguan Pass to the west, there will be no old friends",
"image": "./images/weicheng.jpg",
"fallbackImage": "https://picsum.photos/id/1043/800/600",
"culture": {
"title": "送元二使安西",
"titleEn": "Seeing Off Yuan Er on a Mission to Anxi",
"lines": ["渭城朝雨浥轻尘,客舍青青柳色新", "劝君更尽一杯酒,西出阳关无故人"],
"linesEn": ["Morning rain in Weicheng moistens the light dust; the inn is green with fresh willow color", "I urge you to finish one more cup of wine; beyond Yangguan Pass to the west, there will be no old friends"],
"anecdote": "《送元二使安西》是王维最著名的送别诗之一,又题为《渭城曲》。这首诗语言朴实无华,情感真挚深沉,后被谱成曲子,名为《阳关三叠》,成为唐代送别歌曲的代表作,广为传唱。其中'劝君更尽一杯酒,西出阳关无故人'一句,更是成为表达离别之情的千古名句。",
"anecdoteEn": "'Seeing Off Yuan Er on a Mission to Anxi', also titled 'Weicheng Song', is one of Wang Wei's most famous farewell poems. With simple yet profound language and sincere emotion, this poem was later set to music as 'Yangguan San Die' (Three Refrains of Yangguan), becoming a representative送别 song of the Tang Dynasty and widely sung. The line 'I urge you to finish one more cup of wine; beyond Yangguan Pass to the west, there will be no old friends' has become an immortal quote expressing parting feelings."
}
},
{
"name": "济州",
"nameEn": "Jizhou",
"year": 725,
"stage": 3,
"coord": [36.6, 116.8],
"event": "中进士后任太乐丞,因事贬为济州司仓参军",
"eventEn": "After passing imperial examination, served as Grand Music Administrator, later demoted to Jizhou Storehouse Administrator due to an incident",
"poem": "独在异乡为异客,每逢佳节倍思亲",
"poemEn": "Alone in a foreign land as a stranger; every festive season makes me miss home even more",
"image": "./images/jizhou.jpg",
"fallbackImage": "https://picsum.photos/id/1047/800/600",
"culture": {
"title": "九月九日忆山东兄弟",
"titleEn": "Thinking of My Brothers in Shandong on the Double Ninth Festival",
"lines": ["独在异乡为异客,每逢佳节倍思亲", "遥知兄弟登高处,遍插茱萸少一人"],
"linesEn": ["Alone in a foreign land as a stranger; every festive season makes me miss home even more", "From afar, I know my brothers climb to heights; they all wear cornel, but one is missing"],
"anecdote": "721年,王维进士及第,任太乐丞。后因伶人舞黄狮子事获罪,贬为济州司仓参军。在济州期间,王维远离家乡和亲友,内心充满思乡之情。《九月九日忆山东兄弟》这首脍炙人口的诗篇,正是他在重阳节时思念亲人的真实写照,其中'独在异乡为异客,每逢佳节倍思亲'一句,成为表达游子思乡之情的千古名句。",
"anecdoteEn": "In 721, Wang Wei passed the imperial examination and was appointed as Grand Music Administrator. Later, he was convicted due to the incident of actors dancing with yellow lions and was demoted to Jizhou Storehouse Administrator. During his time in Jizhou, Wang Wei was far away from his hometown and relatives, filled with homesickness. The well-known poem 'Thinking of My Brothers in Shandong on the Double Ninth Festival' is a true portrayal of his yearning for his family during the Double Ninth Festival, and the line 'Alone in a foreign land as a stranger; every festive season makes me miss home even more' has become an immortal quote expressing the longing of travelers for their hometown."
}
},
{
"name": "终南山",
"nameEn": "Zhongnan Mountain",
"year": 734,
"stage": 4,
"coord": [34.0, 108.7],
"event": "返回长安,开始半官半隐的生活,在终南山建辋川别业",
"eventEn": "Returned to Chang'an, began half-official, half-hermit life, built Wangchuan Villa in Zhongnan Mountain",
"poem": "独坐幽篁里,弹琴复长啸",
"poemEn": "Alone sitting in the bamboo grove; playing the zither and whistling long",
"image": "./images/zhongnanshan.jpg",
"fallbackImage": "https://picsum.photos/id/1019/800/600",
"culture": {
"title": "竹里馆",
"titleEn": "Bamboo Lodge",
"lines": ["独坐幽篁里,弹琴复长啸", "深林人不知,明月来相照"],
"linesEn": ["Alone sitting in the bamboo grove; playing the zither and whistling long", "No one knows I'm in the deep forest; the bright moon comes to shine on me"],
"anecdote": "734年,王维返回长安,在张九龄的推荐下任右拾遗。此时的王维开始倾心佛理,追求内心的宁静。他在终南山下建造了辋川别业,过着半官半隐的生活。《竹里馆》生动地描绘了诗人隐居时的闲适生活和内心的宁静。",
"anecdoteEn": "In 734, Wang Wei returned to Chang'an and was appointed as Right Remonstrant under Zhang Jiuling's recommendation. At this time, Wang Wei began to devote himself to Buddhist principles and pursue inner peace. He built Wangchuan Villa at the foot of Zhongnan Mountain and lived a half-official, half-hermit life. 'Bamboo Lodge' vividly depicts the poet's leisurely life and inner peace during his seclusion."
}
},
{
"name": "凉州",
"nameEn": "Liangzhou",
"year": 737,
"stage": 4,
"coord": [37.9, 102.6],
"event": "出使边塞,领略大漠风光",
"eventEn": "Sent as an envoy to the frontier, experiencing the scenery of the desert",
"poem": "大漠孤烟直,长河落日圆",
"poemEn": "The vast desert, a single column of smoke rising straight; the long river, a round setting sun",
"image": "./images/liangzhou.jpg",
"fallbackImage": "https://picsum.photos/id/1036/800/600",
"culture": {
"title": "使至塞上",
"titleEn": "On a Mission to the Frontier",
"lines": ["单车欲问边,属国过居延", "征蓬出汉塞,归雁入胡天", "大漠孤烟直,长河落日圆", "萧关逢候骑,都护在燕然"],
"linesEn": ["Riding alone to visit the frontier; passing Juyan as a vassal state", "Wandering plants blow out of Han's frontier; returning wild geese enter Hu's sky", "The vast desert, a single column of smoke rising straight; the long river, a round setting sun", "Meeting scouts at Xiaoguan Pass; the Protector General is at Yanran"],
"anecdote": "737年,王维以监察御史的身份出使凉州,慰问边军。途中,他写下了著名的《使至塞上》。其中'大漠孤烟直,长河落日圆'两句,以简洁而雄浑的笔触描绘了塞外壮丽的自然风光,被王国维誉为'千古壮观'的名句。",
"anecdoteEn": "In 737, Wang Wei was sent as an imperial censor to Liangzhou to慰问 the frontier troops. On the way, he wrote the famous poem 'On a Mission to the Frontier'. The lines 'The vast desert, a single column of smoke rising straight; the long river, a round setting sun' depict the magnificent natural scenery of the frontier with concise and powerful strokes, praised by Wang Guowei as '千古壮观' (eternally magnificent) lines."
}
},
{
"name": "安禄山叛军占领长安",
"nameEn": "An Lushan's Rebels Occupied Chang'an",
"year": 756,
"stage": 5,
"coord": [34.3, 108.8],
"event": "安史之乱爆发,长安沦陷,王维被俘,被迫接受伪职",
"eventEn": "An Lushan Rebellion broke out, Chang'an fell, Wang Wei was captured and forced to accept a false position",
"poem": "万户伤心生野烟,百官何日再朝天",
"poemEn": "Ten thousand households grieve as wild smoke rises; when will officials pay homage to the emperor again",
"image": "./images/anlushan.jpg",
"fallbackImage": "https://picsum.photos/id/1067/800/600",
"culture": {
"title": "凝碧池",
"titleEn": "Ningbi Pool",
"lines": ["万户伤心生野烟,百官何日再朝天", "秋槐叶落空宫里,凝碧池头奏管弦"],
"linesEn": ["Ten thousand households grieve as wild smoke rises; when will officials pay homage to the emperor again", "Autumn pagoda tree leaves fall in empty palace; orchestral music plays by Ningbi Pool"],
"anecdote": "756年,安史之乱爆发,长安沦陷,王维未能及时随唐玄宗逃亡,被叛军俘获。叛军逼迫王维接受伪职,王维服药装哑以避祸,但未能完全逃脱。在此期间,他写下了《凝碧池》等诗,表达了对唐王朝的忠诚和对叛军的不满。后来唐肃宗收复长安后,因其诗文中的忠君之情,王维得到赦免并继续受到重用。",
"anecdoteEn": "In 756, the An Lushan Rebellion broke out, Chang'an fell, and Wang Wei failed to escape with Emperor Xuanzong in time and was captured by the rebels. The rebels forced Wang Wei to accept a false position. Wang Wei took medicine to pretend to be mute to avoid disaster, but he couldn't completely escape. During this period, he wrote poems like 'Ningbi Pool', expressing his loyalty to the Tang Dynasty and his dissatisfaction with the rebels. Later, after Emperor Suzong recovered Chang'an, Wang Wei was pardoned and continued to be reused because of his loyalty expressed in his poems."
}
},
{
"name": "辋川别业",
"nameEn": "Wangchuan Villa",
"year": 758,
"stage": 6,
"coord": [34.0, 108.7],
"event": "安史之乱后,更加倾心于山水田园和佛理,辋川别业成为其心灵归宿",
"eventEn": "After An Lushan Rebellion, became more devoted to landscape, pastoral life and Buddhist principles; Wangchuan Villa became his spiritual home",
"poem": "明月松间照,清泉石上流",
"poemEn": "Bright moon shines among pine trees; clear spring flows over stones",
"image": "./images/wangchuanbieye.jpg",
"fallbackImage": "https://picsum.photos/id/1039/800/600",
"culture": {
"title": "山居秋暝",
"titleEn": "Autumn Evening in the Mountain Villa",
"lines": ["空山新雨后,天气晚来秋", "明月松间照,清泉石上流", "竹喧归浣女,莲动下渔舟"],
"linesEn": ["Empty mountain after fresh rain; autumn arrives in the evening", "Bright moon shines among pine trees; clear spring flows over stones", "Bamboo rustles as washing girls return; lotus moves as fishing boats pass"],
"anecdote": "安史之乱后,王维虽然仍在朝中任职,但更加倾心于佛理和山水田园生活。他经常居住在辋川别业,与裴迪等友人游山玩水,写诗唱和。《山居秋暝》是王维山水诗的代表作之一,诗中描绘了秋雨过后山村的清新景色,表现了诗人对田园生活的向往和对污浊官场的厌倦。王维的山水诗被苏轼誉为'诗中有画,画中有诗'。",
"anecdoteEn": "After the An Lushan Rebellion, although Wang Wei still held official positions in the court, he became more devoted to Buddhist principles and landscape pastoral life. He often lived in Wangchuan Villa, traveled with friends like Pei Di, and wrote poems in response to each other. 'Autumn Evening in the Mountain Villa' is one of Wang Wei's representative landscape poems. The poem depicts the fresh scenery of the mountain village after autumn rain, expressing the poet's yearning for pastoral life and his weariness of the corrupt officialdom. Su Shi praised Wang Wei's landscape poems as 'there is painting in poetry, and poetry in painting'."
}
},
{
"name": "长安",
"nameEn": "Chang'an",
"year": 760,
"stage": 7,
"coord": [34.3, 108.8],
"event": "官至尚书右丞,世称'王右丞',晚年更加笃信佛教",
"eventEn": "Promoted to Right Assistant Minister of the Ministry of Personnel, known as 'Wang the Right Assistant Minister'; in his later years, he became more devout in Buddhism",
"poem": "人闲桂花落,夜静春山空",
"poemEn": "People are idle, osmanthus flowers fall; night is quiet, spring mountain is empty",
"image": "./images/changan.jpg",
"fallbackImage": "https://picsum.photos/id/1043/800/600",
"culture": {
"title": "鸟鸣涧",
"titleEn": "Birds Singing in the Valley",
"lines": ["人闲桂花落,夜静春山空", "月出惊山鸟,时鸣春涧中"],
"linesEn": ["People are idle, osmanthus flowers fall; night is quiet, spring mountain is empty", "Moonrise startles mountain birds; they sing from time to time in the spring valley"],
"anecdote": "晚年的王维官至尚书右丞,世称'王右丞'。尽管身居高位,但他更加笃信佛教,过着半官半隐的生活。他每日退朝后,焚香独坐,以禅诵为事。《鸟鸣涧》这首诗体现了王维晚年诗歌的禅意特色,通过描绘春夜山涧的宁静景色,表达了诗人内心的空灵和对禅境的追求。王维因此被后人称为'诗佛'。",
"anecdoteEn": "In his later years, Wang Wei was promoted to Right Assistant Minister of the Ministry of Personnel, known as 'Wang the Right Assistant Minister'. Despite his high position, he became more devout in Buddhism and lived a half-official, half-hermit life. Every day after retiring from court, he burned incense and sat alone, engaging in Buddhist chanting. The poem 'Birds Singing in the Valley' reflects the Zen characteristics of Wang Wei's later poetry. By describing the quiet scenery of the mountain valley on a spring night, it expresses the poet's inner emptiness and pursuit of Zen state. Wang Wei was therefore called 'Poet Buddha' by later generations."
}
},
{
"name": "辋川",
"nameEn": "Wangchuan",
"year": 761,
"stage": 7,
"coord": [34.0, 108.7],
"event": "辋川别业中逝世,享年61岁",
"eventEn": "Passed away in Wangchuan Villa at the age of 61",
"poem": "行到水穷处,坐看云起时",
"poemEn": "Walk until the water ends; sit and watch clouds rise",
"image": "./images/wangchuan.jpg",
"fallbackImage": "https://picsum.photos/id/1039/800/600",
"culture": {
"title": "终南别业",
"titleEn": "My Retreat in Zhongnan Mountain",
"lines": ["中岁颇好道,晚家南山陲", "兴来每独往,胜事空自知", "行到水穷处,坐看云起时"],
"linesEn": ["In middle age, I quite favor the Dao; in later years, I settled at the foot of South Mountain", "When inspiration comes, I often go alone; wonderful moments only I know", "Walk until the water ends; sit and watch clouds rise"],
"anecdote": "761年,王维在辋川别业逝世,享年61岁。王维是唐代著名的诗人、画家和音乐家,他的诗歌以山水田园诗为主,风格清新自然,意境深远,充满禅意。他的绘画被称为'南宗之祖',对后世影响深远。王维的一生,既有官场的沉浮,也有山水的滋养,最终在禅意中找到了心灵的归宿。他与李白、杜甫并称为'盛唐三大诗人',被后人誉为'诗佛'。",
"anecdoteEn": "In 761, Wang Wei passed away in Wangchuan Villa at the age of 61. Wang Wei was a famous poet, painter and musician in the Tang Dynasty. His poems mainly focused on landscape and pastoral themes, with fresh and natural style, profound artistic conception and full of Zen. His paintings were called 'the ancestor of the Southern School' and had a profound influence on later generations. Wang Wei's life had both ups and downs in officialdom and nourishment from mountains and rivers, and finally found his spiritual home in Zen. He, together with Li Bai and Du Fu, was known as 'the three great poets of the High Tang Dynasty' and was praised by later generations as 'Poet Buddha'."
}
}
];
/* 初始化地图 */
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 startStage = stageNames.find(s => s.stage === startPoint.stage);
const startColor = startStage ? startStage.color : '#000000';
const endStage = stageNames.find(s => s.stage === endPoint.stage);
const endColor = endStage ? endStage.color : '#000000';
// 如果起点和终点属于同一阶段,直接绘制一条路径
if (startPoint.stage === endPoint.stage) {
const antPath = L.polyline.antPath(
[startPoint.coord, endPoint.coord],
{
"paused": false,
"reverse": false,
"delay": 800,
"dashArray": [8, 12],
"weight": 6,
"color": startColor,
"pulseColor": '#ffffff',
"opacity": 0.9
}
).addTo(map);
pathLines.push(antPath);
} else {
// 如果起点和终点属于不同阶段,在路径中间添加过渡点,绘制两条不同颜色的路径
// 计算中间点坐标
const midPoint = [
(startPoint.coord[0] + endPoint.coord[0]) / 2,
(startPoint.coord[1] + endPoint.coord[1]) / 2
];
// 绘制从起点到中间点的路径(使用起点阶段颜色)
const startAntPath = L.polyline.antPath(
[startPoint.coord, midPoint],
{
"paused": false,
"reverse": false,
"delay": 800,
"dashArray": [8, 12],
"weight": 6,
"color": startColor,
"pulseColor": '#ffffff',
"opacity": 0.9
}
).addTo(map);
// 绘制从中间点到终点的路径(使用终点阶段颜色)
const endAntPath = L.polyline.antPath(
[midPoint, endPoint.coord],
{
"paused": false,
"reverse": false,
"delay": 800,
"dashArray": [8, 12],
"weight": 6,
"color": endColor,
"pulseColor": '#ffffff',
"opacity": 0.9
}
).addTo(map);
pathLines.push(startAntPath, endAntPath);
}
}
}
/* 创建点位标记 */
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: 'highlight-horse-track',
iconSize: [35, 35],
iconAnchor: [17, 17]
});
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 {
// 没有文化信息的情况下,也显示诗题
const poemTitle = '诗句'; // 默认题目
const poemTitleEn = 'Verse'; // 默认英文题目
poem.innerHTML = `<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.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 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>