Expand chronicle to 300 entries with world data
Add a generator script that produces the entries dataset, dedupe entries sharing coordinates, and ship world city and country GeoJSON assets for the globe view. Make the timeline card spacing derive from entry count, move the view switcher into the bottom bar, and align card and label fonts with the timeline titles.
This commit is contained in:
+6
-11
@@ -28,13 +28,6 @@
|
|||||||
<p class="subtitle">活页编年史 · The Loose-leaf Chronicle of Civilisation</p>
|
<p class="subtitle">活页编年史 · The Loose-leaf Chronicle of Civilisation</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- 视图切换 -->
|
|
||||||
<nav class="hud view-switch" aria-label="视图切换">
|
|
||||||
<button class="view-btn active" data-view="binder">活页本</button>
|
|
||||||
<button class="view-btn" data-view="timeline">时间轴</button>
|
|
||||||
<button class="view-btn" data-view="globe">地理</button>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- 底部:导航 -->
|
<!-- 底部:导航 -->
|
||||||
<footer class="hud bottom-bar">
|
<footer class="hud bottom-bar">
|
||||||
<button class="nav-btn" id="btn-prev" title="上一页 (←)">◀</button>
|
<button class="nav-btn" id="btn-prev" title="上一页 (←)">◀</button>
|
||||||
@@ -43,24 +36,26 @@
|
|||||||
<span class="page-name" id="page-name">露西 · 南方古猿</span>
|
<span class="page-name" id="page-name">露西 · 南方古猿</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="nav-btn" id="btn-next" title="下一页 (→)">▶</button>
|
<button class="nav-btn" id="btn-next" title="下一页 (→)">▶</button>
|
||||||
<button class="nav-btn flip-hint" id="btn-flip" title="翻面查看素描">翻面</button>
|
<div class="view-switch" aria-label="视图切换">
|
||||||
|
<button class="view-btn active" data-view="binder">活页本</button>
|
||||||
|
<button class="view-btn" data-view="timeline">时间轴</button>
|
||||||
|
<button class="view-btn" data-view="globe">地理</button>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- 右侧:登记卡详情 -->
|
<!-- 右侧:登记卡详情 -->
|
||||||
<aside class="hud detail-card" id="detail-card">
|
<aside class="hud detail-card" id="detail-card">
|
||||||
<div class="detail-head">
|
<div class="detail-head">
|
||||||
<span class="detail-no" id="detail-no">No.001</span>
|
<span class="detail-no" id="detail-no">No.001</span>
|
||||||
<span class="detail-cat" id="detail-cat">人</span>
|
|
||||||
</div>
|
</div>
|
||||||
<h2 class="detail-name" id="detail-name">露西 · 南方古猿</h2>
|
<h2 class="detail-name" id="detail-name">露西 · 南方古猿</h2>
|
||||||
<dl class="detail-fields">
|
<dl class="detail-fields">
|
||||||
<div class="field"><dt>时间</dt><dd id="detail-time">约公元前320万年</dd></div>
|
<div class="field"><dt>时间</dt><dd id="detail-time">约公元前320万年</dd></div>
|
||||||
<div class="field"><dt>地区</dt><dd id="detail-region">东非 · 埃塞俄比亚</dd></div>
|
<div class="field"><dt>地区</dt><dd id="detail-region">东非 · 埃塞俄比亚</dd></div>
|
||||||
<div class="field"><dt>名称</dt><dd id="detail-name2">露西 · 南方古猿</dd></div>
|
|
||||||
</dl>
|
</dl>
|
||||||
<p class="detail-desc" id="detail-desc"></p>
|
<p class="detail-desc" id="detail-desc"></p>
|
||||||
<div class="detail-sketch">
|
<div class="detail-sketch">
|
||||||
<canvas id="detail-sketch-canvas" width="560" height="320"></canvas>
|
<canvas id="detail-sketch-canvas" width="360" height="200"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,421 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""生成 src/data/entries.js 的 300 条历史词条。字段顺序:
|
||||||
|
(time, timeNumeric, region, name, category, sketch, lat, lng, description)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
DATA = [
|
||||||
|
# ===================== 史前 =====================
|
||||||
|
("~ BC 3,200,000", -3200000, "东非 · 埃塞俄比亚", "露西 · 南方古猿", "人", "fossil", 11.2, 40.5, "1974年在埃塞俄比亚哈达尔发现的一具约40%完整的南方古猿阿法种骨架,是直立行走的最早证据之一,被亲切地称为“人类祖母”。"),
|
||||||
|
("~ BC 3,300,000", -3300000, "东非 · 肯尼亚", "洛梅奎石器", "物", "tablet", 3.3, 35.7, "2015年在肯尼亚洛梅奎发现的最古老石制工具,将人类制造工具的历史推进到约330万年前,早于人属出现。"),
|
||||||
|
("~ BC 2,600,000", -2600000, "东非 · 坦桑尼亚", "奥杜威砍砸器", "物", "tablet", -2.98, 35.35, "奥杜威峡谷出土的早期石器,标志着能人开始系统打制工具,开启了旧石器时代的奥杜威文化。"),
|
||||||
|
("~ BC 1,700,000", -1700000, "东非 · 肯尼亚", "阿舍利手斧", "物", "tablet", -0.3, 36.0, "对称打制的泪滴状手斧,是阿舍利文化的标志,体现早期人类对工具形制的规划能力。"),
|
||||||
|
("~ BC 1,000,000", -1000000, "东亚 · 中国周口店", "北京猿人用火", "事", "fossil", 39.7, 115.9, "周口店遗址的灰烬层表明北京猿人已能控制并保存火种,火的使用深刻改变了人类的饮食与生存方式。"),
|
||||||
|
("~ BC 300,000", -300000, "北非 · 摩洛哥", "杰贝尔依罗智人化石", "人", "fossil", 31.9, -8.7, "摩洛哥杰贝尔依罗遗址发现的智人化石距今约30万年,将现代人类的起源时间大幅提前。"),
|
||||||
|
("~ BC 100,000", -100000, "西亚 · 以色列", "最早的象征符号", "物", "tablet", 32.7, 34.9, "以色列卡夫扎等地出土的贝壳串珠与赭石,是最早的象征性装饰与颜料使用证据之一。"),
|
||||||
|
("~ BC 60,000", -60000, "西亚 · 伊拉克", "尼安德特人墓葬", "事", "fossil", 36.8, 43.3, "沙尼达尔洞穴的尼安德特人墓葬伴花与陪葬品,暗示早期人类已有死亡仪式与情感关怀。"),
|
||||||
|
("~ BC 40,000", -40000, "欧洲 · 德国", "狮子人雕像", "物", "fossil", 48.4, 9.9, "德国霍伦斯坦-施塔德尔出土的猛犸象牙雕“狮子人”,是已知最早的具象艺术与想象造物。"),
|
||||||
|
("~ BC 25,000", -25000, "欧洲 · 奥地利", "维伦多夫维纳斯", "物", "fossil", 48.3, 15.6, "这尊石灰岩小雕像夸张母性特征,是旧石器时代丰产崇拜与早期雕塑艺术的代表。"),
|
||||||
|
("~ BC 17,000", -17000, "欧洲 · 法国", "拉斯科洞窟壁画", "物", "fossil", 45.05, 1.17, "法国拉斯科洞窟的野牛、马群彩绘,是旧石器时代最壮观的洞穴艺术,被誉为“史前西斯廷”。"),
|
||||||
|
("~ BC 12,000", -12000, "日本 · 本州", "绳文陶器", "物", "tablet", 36.0, 140.0, "绳文人制作的绳纹陶罐,是世界最早的陶器之一,标志人类开始定居与储存食物。"),
|
||||||
|
("~ BC 10,000", -10000, "西亚 · 两河流域", "农业革命", "事", "scroll", 36.5, 43.0, "新月沃地的先民驯化小麦、大麦与豆类,从采集狩猎转向定居农耕,奠定文明诞生的基础。"),
|
||||||
|
("~ BC 9,500", -9500, "西亚 · 土耳其", "哥贝克力石阵", "物", "temple", 37.2, 38.9, "哥贝克力石阵的巨石立柱环阵早于农业,是已知最古老的大型祭祀建筑,改写宗教起源史。"),
|
||||||
|
("~ BC 7,000", -7000, "西亚 · 土耳其", "加泰土丘聚落", "事", "pyramid", 37.7, 32.8, "加泰土丘的密集泥砖房屋与壁画,展示新石器时代最早的城镇生活与母神崇拜。"),
|
||||||
|
("~ BC 3,500", -3500, "南亚 · 印度河谷", "印度河文明兴起", "事", "pyramid", 27.3, 68.1, "摩亨佐-达罗与哈拉帕等城市兴起,印度河文明拥有完善的下水道与未解的文字系统。"),
|
||||||
|
("~ BC 3,200", -3200, "美索不达米亚 · 两河流域", "楔形文字", "物", "tablet", 31.3, 45.6, "苏美尔人用削尖的芦苇杆在湿泥板上压刻楔形符号,是人类已知最早的文字系统,也是文明记录的开端。"),
|
||||||
|
("~ BC 3,100", -3100, "古埃及 · 孟菲斯", "那尔迈调色板", "物", "tablet", 29.85, 31.25, "这块调色板记录那尔迈统一上下埃及,被视为法老时代与埃及王权确立的标志。"),
|
||||||
|
("~ BC 3,000", -3000, "古埃及 · 阿拜多斯", "古埃及象形文字", "物", "tablet", 26.2, 31.9, "古埃及人创制象形文字,与楔形文字并列为人类最早的书写系统之一,沿用三千余年。"),
|
||||||
|
("~ BC 2,700", -2700, "古埃及 · 萨卡拉", "左塞尔阶梯金字塔", "物", "pyramid", 29.87, 31.22, "建筑师伊姆霍特普为法老左塞尔建造的阶梯金字塔,是最早的石砌大型纪念建筑。"),
|
||||||
|
("~ BC 2,560", -2560, "古埃及 · 吉萨", "胡夫金字塔", "物", "pyramid", 29.98, 31.13, "埃及第四王朝法老胡夫的陵墓,由约230万块巨石砌成,是古代世界七大奇迹中唯一留存至今的建筑。"),
|
||||||
|
("~ BC 2,500", -2500, "不列颠 · 威尔特郡", "巨石阵", "物", "temple", 51.18, -1.83, "巨石阵的巨大砂岩与蓝石环,历经千年营造,是史前天文观测与祭祀中心的未解之谜。"),
|
||||||
|
("~ BC 2,100", -2100, "美索不达米亚 · 乌尔", "乌尔金字形神塔", "物", "temple", 30.96, 46.1, "苏美尔人为月神南纳建造的阶梯神塔,是两河流域宗教建筑的典范。"),
|
||||||
|
("~ BC 2,100", -2100, "美索不达米亚 · 苏美尔", "吉尔伽美什史诗", "物", "scroll", 31.3, 45.6, "这部楔形文字史诗讲述乌鲁克王吉尔伽美什的冒险,是已知最早的英雄史诗与文学作品。"),
|
||||||
|
("~ BC 1,754", -1754, "美索不达米亚 · 巴比伦", "汉谟拉比法典", "物", "tablet", 32.5, 44.4, "巴比伦王汉谟拉比颁布的282条法典,刻于玄武岩石碑,是“以眼还眼”成文法的代表。"),
|
||||||
|
("~ BC 1,600", -1600, "爱琴海 · 克里特岛", "米诺斯宫殿", "物", "temple", 35.3, 25.16, "克里特岛克诺索斯的米诺斯王宫,廊柱交错、壁画绚丽,是爱琴文明的中心。"),
|
||||||
|
("~ BC 1,600", -1600, "中国 · 河南安阳", "甲骨文", "物", "tablet", 36.1, 114.3, "商代刻在龟甲兽骨上的占卜文字,是中国已发现最早的成体系文字,见证汉字起源。"),
|
||||||
|
("~ BC 1,500", -1500, "古印度 · 恒河流域", "吠陀经典", "物", "scroll", 29.0, 78.0, "印度雅利安人编纂的《梨俱吠陀》等经典,奠定印度教与种姓制度的思想源头。"),
|
||||||
|
("~ BC 1,350", -1350, "古埃及 · 阿玛纳", "图坦卡蒙黄金面具", "物", "crown", 25.7, 32.6, "法老图坦卡蒙墓中出土的黄金面具,是古埃及工艺与法老崇拜的巅峰之作。"),
|
||||||
|
("~ BC 1,250", -1250, "西亚 · 安纳托利亚", "赫梯铁器", "物", "machine", 39.9, 34.8, "赫梯人最早大规模冶炼铁器,铁制武器与农具推动青铜时代向铁器时代过渡。"),
|
||||||
|
("~ BC 1,200", -1200, "地中海 · 东岸", "青铜时代大崩溃", "事", "colosseum", 33.5, 36.3, "海上民族入侵与城邦衰落引发地中海文明的整体崩塌,迈锡尼、赫梯等王国相继灭亡。"),
|
||||||
|
("~ BC 1,046", -1046, "中国 · 陕西西安", "武王克商", "事", "crown", 34.3, 108.9, "周武王伐纣灭商,建立西周,开启分封制与礼乐文明,奠定中国三千年的王朝传统。"),
|
||||||
|
("~ BC 1,000", -1000, "中南美 · 墨西哥湾", "奥尔梅克巨石头像", "物", "fossil", 18.1, -94.4, "奥尔梅克人雕凿的巨型玄武岩头像,是中美洲最早的文明与“母文化”的象征。"),
|
||||||
|
("~ BC 776", -776, "古希腊 · 奥林匹亚", "第一届奥林匹克运动会", "事", "colosseum", 37.6, 21.6, "古希腊人在奥林匹亚举办祭祀宙斯的竞技会,成为古代奥运会与体育竞技传统的起点。"),
|
||||||
|
("~ BC 753", -753, "意大利 · 罗马", "罗马建城", "事", "colosseum", 41.9, 12.5, "相传罗慕路斯在台伯河畔建立罗马城,此后罗马从七丘小邦成长为地中海霸主。"),
|
||||||
|
("~ BC 700", -700, "古希腊 · 爱奥尼亚", "荷马史诗", "物", "scroll", 38.0, 27.0, "《伊利亚特》与《奥德赛》相传为盲诗人荷马所作,是西方文学的源头。"),
|
||||||
|
("~ BC 594", -594, "古希腊 · 雅典", "梭伦改革", "事", "scale", 37.98, 23.73, "梭伦废除债务奴隶、按财产划分等级,为雅典民主政治奠定基础。"),
|
||||||
|
("~ BC 563", -563, "古印度 · 蓝毗尼", "释迦牟尼", "人", "figure", 27.5, 83.3, "佛教创始人,在菩提树下觉悟成道,其四谛、八正道思想深刻影响亚洲文明。"),
|
||||||
|
("551 BC", -551, "中国 · 鲁国曲阜", "孔子", "人", "figure", 35.6, 117.0, "儒家学派创始人,倡导仁、礼与中庸之道,其思想深刻影响东亚两千余年,被尊为“至圣先师”。"),
|
||||||
|
("~ BC 539", -539, "西亚 · 巴比伦", "居鲁士圆柱", "物", "scroll", 32.5, 44.4, "波斯居鲁士大帝颁布的圆柱诏书,被视为最早的人权宣言,体现宽容统治。"),
|
||||||
|
("~ BC 509", -509, "罗马 · 意大利", "罗马共和国建立", "事", "scale", 41.9, 12.5, "罗马人驱逐末代国王,建立元老院与执政官制度,开启近五百年的共和国时代。"),
|
||||||
|
("~ BC 490", -490, "古希腊 · 马拉松", "马拉松战役", "事", "colosseum", 38.1, 23.9, "雅典军队在马拉松平原击败波斯入侵者,传令兵长跑报捷成为马拉松赛的起源。"),
|
||||||
|
("~ BC 480", -480, "古希腊 · 温泉关", "温泉关战役", "事", "colosseum", 38.8, 22.5, "斯巴达王列奥尼达率三百勇士扼守温泉关,悲壮阻击波斯大军,成为勇气象征。"),
|
||||||
|
("~ BC 469", -469, "古希腊 · 雅典", "苏格拉底", "人", "figure", 37.98, 23.73, "古希腊哲学家,以问答法探求真理,为雅典民主所不容而饮鸩殉道,是西方哲学奠基人。"),
|
||||||
|
("5th century BC", -450, "古希腊 · 雅典", "雅典卫城 · 帕特农神庙", "物", "temple", 37.97, 23.73, "矗立于雅典城邦山丘之上的建筑群,帕特农神庙为其核心,是古希腊文明、民主精神与古典美学的永恒象征。"),
|
||||||
|
("5th century BC", -450, "古希腊 · 雅典", "希罗多德《历史》", "物", "scroll", 37.98, 23.73, "希罗多德记述希波战争与各地风土,被西塞罗誉为“历史之父”,开创史学传统。"),
|
||||||
|
("~ BC 427", -427, "古希腊 · 雅典", "柏拉图", "人", "figure", 37.98, 23.73, "苏格拉底弟子,创办雅典学园,其“理想国”与理念论深刻塑造西方哲学两千余年。"),
|
||||||
|
("~ BC 384", -384, "古希腊 · 斯塔基拉", "亚里士多德", "人", "figure", 40.5, 23.8, "古希腊百科全书式学者,创立逻辑学并系统研究自然,是亚历山大大帝的老师。"),
|
||||||
|
("334 BC", -334, "马其顿 · 希腊半岛", "亚历山大大帝", "人", "figure", 40.7, 22.5, "马其顿国王,率军东征波斯,兵锋直抵印度河流域,建立横跨欧亚非的庞大帝国,促进了希腊化文明的广泛传播。"),
|
||||||
|
("~ BC 300", -300, "古埃及 · 亚历山大港", "亚历山大图书馆", "物", "scroll", 31.2, 29.9, "托勒密王朝在亚历山大港建立的古代最大图书馆,汇聚地中海世界的典籍与学者。"),
|
||||||
|
("~ BC 300", -300, "古希腊 · 亚历山大港", "欧几里得《几何原本》", "物", "scroll", 31.2, 29.9, "欧几里得系统整理几何学公理体系,两千余年一直是数学教育的经典范本。"),
|
||||||
|
("~ BC 287", -287, "古希腊 · 叙拉古", "阿基米德", "人", "figure", 37.1, 15.3, "古希腊科学家,发现浮力定律与杠杆原理,是古代最伟大的数学家与物理学家之一。"),
|
||||||
|
("~ BC 273", -273, "古印度 · 华氏城", "阿育王", "人", "crown", 25.6, 85.1, "孔雀王朝君主,皈依佛教后以法轮与石柱弘扬佛法,将佛教推向整个南亚。"),
|
||||||
|
("221 BC", -221, "中国 · 咸阳", "秦始皇统一六国", "事", "crown", 34.34, 108.94, "嬴政扫灭六国,建立中国历史上第一个中央集权的大一统王朝,推行书同文、车同轨。"),
|
||||||
|
("~ BC 214", -214, "中国 · 北方边塞", "万里长城", "物", "pyramid", 40.4, 116.9, "秦始皇连接战国长城抵御匈奴,成为人类史上规模最大的军事防御工程。"),
|
||||||
|
("~ BC 210", -210, "中国 · 陕西西安", "秦始皇兵马俑", "物", "fossil", 34.38, 109.27, "秦始皇陵陪葬的八千陶俑军阵,千人千面,被誉为“世界第八大奇迹”。"),
|
||||||
|
("~ BC 206", -206, "中国 · 陕西汉中", "汉朝建立", "事", "crown", 33.1, 107.0, "刘邦击败项羽建立汉朝,开启四百年大汉王朝,奠定中华文明的主体格局。"),
|
||||||
|
("~ BC 200", -200, "中国 · 长安", "丝绸之路开通", "事", "ship", 34.3, 108.9, "张骞出使西域打通欧亚陆上通道,丝绸、技术、宗教沿这条大动脉往来两千年。"),
|
||||||
|
("~ BC 146", -146, "北非 · 迦太基", "迦太基灭亡", "事", "colosseum", 36.9, 10.3, "第三次布匿战争后罗马夷平迦太基,确立地中海霸权,北非纳入罗马版图。"),
|
||||||
|
("~ BC 141", -141, "中国 · 长安", "汉武帝即位", "事", "crown", 34.3, 108.9, "汉武帝北击匈奴、凿空西域、独尊儒术,使汉朝达到鼎盛,塑造中华帝国模式。"),
|
||||||
|
("~ BC 100", -100, "中国 · 四川", "造纸术萌芽", "物", "scroll", 30.5, 104.0, "西汉时期已出现麻纤维纸,为东汉蔡伦改进造纸术奠定基础。"),
|
||||||
|
("~ BC 73", -73, "古罗马 · 卡普阿", "斯巴达克斯起义", "事", "colosseum", 41.1, 14.2, "角斗士斯巴达克斯率奴隶起义,震动罗马共和国,成为反抗奴役的千古象征。"),
|
||||||
|
("~ BC 44", -44, "古罗马 · 罗马", "凯撒遇刺", "事", "colosseum", 41.9, 12.5, "独裁官凯撒在元老院遇刺身亡,罗马陷入内战,共和国走向终结。"),
|
||||||
|
("~ BC 27", -27, "古罗马 · 罗马", "奥古斯都建立元首制", "事", "crown", 41.9, 12.5, "屋大维获“奥古斯都”尊号,建立元首制,罗马由共和国转型为帝国,开启两百年和平。"),
|
||||||
|
("~ BC 4", -4, "犹太 · 伯利恒", "耶稣诞生", "人", "figure", 31.7, 35.2, "基督教创始人耶稣诞生于伯利恒,其福音与受难复活成为世界最大宗教的根基。"),
|
||||||
|
("~ BC 2", -2, "中国 · 长安", "王莽改制", "事", "scale", 34.3, 108.9, "王莽代汉建新朝,推行托古改制与土地国有,因脱离现实而迅速崩溃。"),
|
||||||
|
("AD 25", 25, "中国 · 洛阳", "东汉建立", "事", "crown", 34.6, 112.4, "光武帝刘秀重建汉室,定都洛阳,史称东汉,儒学与经学再度兴盛。"),
|
||||||
|
("AD 33", 33, "犹太 · 耶路撒冷", "基督教创立", "事", "temple", 31.78, 35.23, "耶稣受难并复活,使徒开始传播福音,基督教从此兴起并改变世界。"),
|
||||||
|
("AD 70", 70, "犹太 · 耶路撒冷", "第二圣殿被毁", "事", "temple", 31.78, 35.23, "罗马军团攻陷耶路撒冷并焚毁第二圣殿,犹太人流散四方,改变犹太教形态。"),
|
||||||
|
("AD 79", 79, "古罗马 · 庞贝", "维苏威火山爆发", "事", "fossil", 40.75, 14.49, "维苏威火山喷发掩埋庞贝与赫库兰尼姆,火山灰封存了古罗马城市生活原貌。"),
|
||||||
|
("AD 80", 80, "罗马帝国 · 罗马", "罗马斗兽场", "物", "colosseum", 41.89, 12.49, "古罗马最大的椭圆形竞技场,可容纳约五万名观众,是拱券结构与帝国工程的杰作。"),
|
||||||
|
("AD 105", 105, "中国 · 东汉洛阳", "蔡伦造纸", "事", "scroll", 34.6, 112.4, "蔡伦以树皮、麻头、破布、渔网造出轻便廉价的纸,深刻改变知识的记录与传播方式。"),
|
||||||
|
("AD 132", 132, "中国 · 洛阳", "张衡地动仪", "物", "machine", 34.6, 112.4, "张衡创制候风地动仪,是世界上最早的地震检测仪器,体现东汉科技的精密。"),
|
||||||
|
("AD 220", 220, "中国 · 洛阳", "三国鼎立", "事", "crown", 34.6, 112.4, "曹丕代汉建魏,蜀汉、孙吴相继而立,三国割据的时代由此展开。"),
|
||||||
|
("AD 250", 250, "中美洲 · 墨西哥", "玛雅古典期繁荣", "事", "pyramid", 17.2, -89.5, "蒂卡尔等玛雅城邦进入古典期鼎盛,象形文字、历法与金字塔建筑高度发达。"),
|
||||||
|
("AD 313", 313, "古罗马 · 米兰", "米兰敕令", "事", "temple", 45.46, 9.19, "君士坦丁颁布米兰敕令,基督教获得合法地位,从被迫害宗教转向帝国宗教。"),
|
||||||
|
("AD 330", 330, "拜占庭 · 君士坦丁堡", "君士坦丁堡建城", "事", "crown", 41.01, 28.98, "君士坦丁大帝在拜占庭故址营建新都,东罗马帝国从此千年不坠。"),
|
||||||
|
("AD 380", 380, "罗马帝国 · 塞萨洛尼基", "基督教成为国教", "事", "temple", 40.64, 22.94, "狄奥多西颁布敕令,将尼西亚基督教立为罗马国教,奠定欧洲基督教文明。"),
|
||||||
|
("AD 395", 395, "罗马帝国 · 罗马", "罗马帝国分裂", "事", "scale", 41.9, 12.5, "狄奥多西死后帝国分为东西两部,西罗马迅速衰落,东罗马延续千年。"),
|
||||||
|
("AD 476", 476, "西罗马 · 拉文纳", "西罗马帝国灭亡", "事", "colosseum", 44.42, 12.2, "日耳曼首领奥多亚克废黜末帝,西罗马灭亡,西欧进入中世纪。"),
|
||||||
|
("AD 527", 527, "拜占庭 · 君士坦丁堡", "查士丁尼大帝", "人", "crown", 41.01, 28.98, "拜占庭皇帝,编纂《查士丁尼法典》并力图光复罗马故土,是拜占庭黄金时代。"),
|
||||||
|
("AD 529", 529, "拜占庭 · 君士坦丁堡", "《查士丁尼法典》", "物", "scroll", 41.01, 28.98, "系统汇编罗马法而成的法典,成为后世大陆法系的根基。"),
|
||||||
|
("AD 537", 537, "拜占庭 · 君士坦丁堡", "圣索菲亚大教堂", "物", "temple", 41.01, 28.98, "查士丁尼建造的圣索菲亚大教堂,巨大穹顶是拜占庭建筑的巅峰。"),
|
||||||
|
("~AD 570", 570, "阿拉伯半岛 · 麦加", "穆罕默德", "人", "figure", 21.42, 39.83, "伊斯兰教先知,在阿拉伯半岛传播一神信仰,其创立的伊斯兰教深刻塑造了中东、北非乃至世界的文明版图。"),
|
||||||
|
("AD 589", 589, "中国 · 建康", "隋朝统一", "事", "crown", 32.06, 118.8, "隋文帝灭陈,结束近三百年的南北分裂,中国重新归于大一统。"),
|
||||||
|
("AD 605", 605, "中国 · 洛阳", "大运河开凿", "物", "bridge", 34.6, 112.4, "隋炀帝开凿贯通南北的大运河,连接海河、黄河、淮河、长江、钱塘江五大水系。"),
|
||||||
|
("AD 618", 618, "中国 · 长安", "唐朝建立", "事", "crown", 34.3, 108.9, "李渊建唐,随后李世民开创贞观之治,唐朝成为当时世界最强盛的帝国。"),
|
||||||
|
("AD 622", 622, "阿拉伯半岛 · 麦地那", "希吉拉", "事", "temple", 24.47, 39.61, "穆罕默德从麦加迁徙麦地那,标志着伊斯兰历元年与穆斯林公社的建立。"),
|
||||||
|
("AD 630", 630, "阿拉伯半岛 · 麦加", "麦加光复", "事", "temple", 21.42, 39.83, "穆罕默德和平进入麦加,清除偶像,克尔白天房成为伊斯兰教朝觐中心。"),
|
||||||
|
("AD 645", 645, "中国 · 长安", "玄奘西行归来", "事", "ship", 34.3, 108.9, "玄奘历时十七年西行取经,译经千卷并著《大唐西域记》,是中印交流的丰碑。"),
|
||||||
|
("AD 651", 651, "波斯 · 萨珊", "萨珊波斯灭亡", "事", "crown", 32.0, 53.0, "阿拉伯军队灭亡萨珊波斯,波斯皈依伊斯兰,西亚格局剧变。"),
|
||||||
|
("AD 660", 660, "朝鲜半岛 · 百济", "新罗统一三国", "事", "crown", 35.1, 126.9, "新罗联合唐朝灭亡百济、高句丽,统一朝鲜半岛大部,奠定朝鲜民族格局。"),
|
||||||
|
("AD 711", 711, "伊比利亚 · 直布罗陀", "穆斯林征服西班牙", "事", "ship", 36.1, -5.3, "阿拉伯与柏柏尔联军渡过直布罗陀海峡,征服伊比利亚,开启安达卢斯文明。"),
|
||||||
|
("AD 732", 732, "法兰克 · 图尔", "图尔战役", "事", "sword", 47.39, 0.69, "查理·马特在图尔击败穆斯林军队,遏止伊斯兰势力深入西欧腹地。"),
|
||||||
|
("AD 751", 751, "中亚 · 怛罗斯", "怛罗斯之战", "事", "sword", 42.9, 71.3, "唐军与阿拔斯军队在中亚交战,造纸术由此西传阿拉伯世界。"),
|
||||||
|
("AD 768", 768, "法兰克 · 亚琛", "查理曼大帝", "人", "crown", 50.78, 6.08, "法兰克国王,统一西欧大部,800年加冕为“罗马人的皇帝”,被誉为欧洲之父。"),
|
||||||
|
("AD 794", 794, "日本 · 京都", "平安京建都", "事", "crown", 35.01, 135.77, "桓武天皇迁都平安京,开启日本平安时代,贵族文化与物语文学繁荣。"),
|
||||||
|
("AD 800", 800, "罗马 · 意大利", "查理曼加冕", "事", "crown", 41.9, 12.5, "教皇利奥三世为查理曼加冕,象征西欧政教合一与神圣罗马帝国传统的开端。"),
|
||||||
|
("AD 843", 843, "法兰克 · 凡尔登", "《凡尔登条约》", "事", "scale", 49.16, 5.4, "查理曼帝国一分为三,奠定后世法兰西、德意志、意大利的雏形。"),
|
||||||
|
("AD 868", 868, "中国 · 敦煌", "《金刚经》雕版印刷", "物", "scroll", 40.1, 94.7, "敦煌藏经洞发现的《金刚经》,是世界现存最早有明确纪年的雕版印刷品。"),
|
||||||
|
("AD 960", 960, "中国 · 开封", "宋朝建立", "事", "crown", 34.8, 114.3, "赵匡胤陈桥兵变建宋,结束五代十国,经济、科技与市井文化空前繁荣。"),
|
||||||
|
("AD 1000", 1000, "北欧 · 格陵兰", "维京人抵达美洲", "事", "ship", 64.2, -51.7, "莱夫·埃里克松率维京人抵达北美文兰,早于哥伦布近五百年。"),
|
||||||
|
("AD 1040", 1040, "中国 · 北宋", "毕昇活字印刷", "物", "scroll", 30.2, 120.2, "毕昇发明胶泥活字,实现可重复使用的排版印刷,是世界印刷史上的革命。"),
|
||||||
|
("AD 1054", 1054, "拜占庭 · 君士坦丁堡", "东西教会大分裂", "事", "temple", 41.01, 28.98, "罗马与君士坦丁堡教会互相绝罚,基督教正式分裂为天主教与东正教。"),
|
||||||
|
("AD 1066", 1066, "英格兰 · 黑斯廷斯", "诺曼征服", "事", "sword", 50.9, 0.5, "诺曼底公爵威廉在黑斯廷斯击败英军,征服英格兰,深刻改变英国政治与语言。"),
|
||||||
|
("AD 1088", 1088, "意大利 · 博洛尼亚", "博洛尼亚大学", "物", "book", 44.49, 11.34, "欧洲最早的大学在博洛尼亚创立,开启中世纪大学的学术自治传统。"),
|
||||||
|
("AD 1096", 1096, "西欧 · 克莱蒙", "第一次十字军东征", "事", "sword", 45.78, 3.08, "教皇乌尔班二世号召东征,十字军夺回耶路撒冷,开启两百年的宗教战争。"),
|
||||||
|
("AD 1127", 1127, "中国 · 临安", "南宋建立", "事", "crown", 30.25, 120.16, "靖康之变后赵构南渡建南宋,经济重心南移,江南成为中华文明核心。"),
|
||||||
|
("AD 1150", 1150, "柬埔寨 · 吴哥", "吴哥窟", "物", "temple", 13.41, 103.87, "高棉国王苏利耶跋摩二世建造的吴哥窟,是世界最大的宗教建筑群。"),
|
||||||
|
("AD 1163", 1163, "法国 · 巴黎", "巴黎圣母院", "物", "temple", 48.85, 2.35, "哥特式建筑的开山之作巴黎圣母院动工,飞扶壁与玫瑰窗开创建筑新纪元。"),
|
||||||
|
("AD 1206", 1206, "蒙古高原", "成吉思汗", "人", "horseman", 47.9, 106.9, "铁木真统一蒙古各部,建立大蒙古国,其子孙建立人类史上疆域最辽阔的陆上帝国。"),
|
||||||
|
("AD 1215", 1215, "英格兰 · 兰尼米德", "《大宪章》", "物", "scale", 51.4, -0.6, "英王约翰被迫签署《大宪章》,王权首次受法律约束,是宪政与法治的基石。"),
|
||||||
|
("AD 1271", 1271, "中国 · 大都", "元朝建立", "事", "crown", 39.9, 116.4, "忽必烈改国号为元,定都大都,中国首次由少数民族建立全国性统一王朝。"),
|
||||||
|
("AD 1275", 1275, "中国 · 大都", "马可·波罗来华", "事", "ship", 39.9, 116.4, "威尼斯商人马可·波罗抵达元朝,其游记激发欧洲对东方的向往与探索。"),
|
||||||
|
("AD 1300", 1300, "意大利 · 佛罗伦萨", "文艺复兴兴起", "事", "book", 43.77, 11.26, "佛罗伦萨等地兴起人文主义,复兴古典文化,开启从中世纪向近代的转型。"),
|
||||||
|
("AD 1337", 1337, "欧洲 · 英法", "百年战争", "事", "sword", 48.5, 2.0, "英法为争夺王位与领土爆发百年战争,民族国家意识在战火中觉醒。"),
|
||||||
|
("AD 1347", 1347, "欧洲 · 意大利", "黑死病大流行", "事", "fossil", 45.4, 10.0, "鼠疫席卷欧洲,约三分之一人口死亡,深刻动摇中世纪社会与教会权威。"),
|
||||||
|
("AD 1368", 1368, "中国 · 南京", "明朝建立", "事", "crown", 32.06, 118.8, "朱元璋驱逐蒙元建立明朝,恢复汉族统治,重建中央集权体制。"),
|
||||||
|
("AD 1405", 1405, "中国 · 南京", "郑和下西洋", "事", "ship", 32.06, 118.8, "郑和率庞大船队七下西洋,远达东非,是古代规模最大的远洋航行。"),
|
||||||
|
("AD 1415", 1415, "葡萄牙 · 休达", "葡萄牙海外扩张", "事", "ship", 35.9, -5.3, "葡萄牙攻占北非休达,拉开欧洲大航海时代与海外殖民的序幕。"),
|
||||||
|
("AD 1431", 1431, "法国 · 鲁昂", "圣女贞德殉难", "人", "flag", 49.44, 1.1, "奥尔良少女贞德领导法军抗英,被俘后殉难,成为法国民族精神的象征。"),
|
||||||
|
("AD 1440", 1440, "德意志 · 美因茨", "古腾堡印刷术", "物", "scroll", 49.99, 8.27, "古腾堡发明金属活字印刷机,书籍量产使知识传播空前加速。"),
|
||||||
|
("AD 1453", 1453, "拜占庭 · 君士坦丁堡", "君士坦丁堡陷落", "事", "colosseum", 41.01, 28.98, "奥斯曼苏丹穆罕默德二世攻陷君士坦丁堡,千年拜占庭帝国灭亡,中世纪终结。"),
|
||||||
|
("AD 1492", 1492, "西班牙 · 格拉纳达", "收复失地运动完成", "事", "sword", 37.18, -3.6, "西班牙双王攻克格拉纳达,完成对伊比利亚的再征服,终结穆斯林统治。"),
|
||||||
|
("AD 1492", 1492, "西班牙 → 美洲", "哥伦布发现新大陆", "事", "ship", 24.0, -75.0, "哥伦布率三艘帆船横渡大西洋抵达美洲,揭开大航海时代与全球联系的序幕。"),
|
||||||
|
("AD 1494", 1494, "大西洋 · 托德西利亚斯", "《托德西利亚斯条约》", "事", "scale", 41.5, -5.0, "教皇仲裁西葡瓜分新世界,划定教皇子午线,塑造拉美殖民格局。"),
|
||||||
|
("AD 1498", 1498, "印度 · 卡利卡特", "达·伽马抵达印度", "事", "ship", 11.25, 75.78, "达·伽马绕过好望角抵达印度,开辟欧洲直达亚洲的海上新航路。"),
|
||||||
|
("AD 1502", 1502, "意大利 · 佛罗伦萨", "《蒙娜丽莎》", "物", "figure", 43.77, 11.26, "达·芬奇创作《蒙娜丽莎》,其晕涂法与神秘微笑成为文艺复兴艺术象征。"),
|
||||||
|
("AD 1508", 1508, "意大利 · 罗马", "西斯廷天顶画", "物", "figure", 41.9, 12.45, "米开朗基罗在西斯廷教堂绘制天顶壁画,是文艺复兴艺术的巅峰杰作。"),
|
||||||
|
("AD 1517", 1517, "德意志 · 维滕贝格", "马丁·路德宗教改革", "事", "scroll", 51.87, 12.64, "马丁·路德张贴《九十五条论纲》,宗教改革席卷欧洲,新教从此兴起。"),
|
||||||
|
("AD 1519", 1519, "中美洲 · 墨西哥", "科尔特斯征服阿兹特克", "事", "sword", 19.43, -99.13, "西班牙征服者科尔特斯灭亡阿兹特克帝国,中美洲文明遭毁灭性打击。"),
|
||||||
|
("AD 1519—1522", 1519, "欧洲 → 环球", "麦哲伦环球航行", "事", "globe", 53.5, -70.8, "麦哲伦船队首次完成环绕地球一周的航行,实证地球是圆的,人类世界观从此改变。"),
|
||||||
|
("AD 1526", 1526, "印度 · 帕尼帕特", "莫卧儿帝国建立", "事", "crown", 29.4, 77.0, "巴布尔在帕尼帕特击败德里苏丹,建立莫卧儿帝国,印度进入新的黄金时代。"),
|
||||||
|
("AD 1533", 1533, "南美 · 秘鲁", "皮萨罗征服印加", "事", "sword", -13.5, -72.0, "皮萨罗俘虏印加王阿塔瓦尔帕,灭亡印加帝国,安第斯文明戛然而止。"),
|
||||||
|
("AD 1543", 1543, "波兰 · 弗龙堡", "哥白尼日心说", "物", "globe", 54.36, 19.68, "哥白尼《天体运行论》提出日心说,动摇地心说,开启近代科学革命。"),
|
||||||
|
("AD 1555", 1555, "德意志 · 奥格斯堡", "《奥格斯堡和约》", "事", "scale", 48.37, 10.9, "神圣罗马帝国确立“教随国定”原则,承认路德宗合法地位。"),
|
||||||
|
("AD 1588", 1588, "英吉利海峡", "击败无敌舰队", "事", "ship", 50.2, -3.0, "英国海军击败西班牙无敌舰队,西班牙海上霸权衰落,英国崛起。"),
|
||||||
|
("AD 1600", 1600, "英格兰 · 伦敦", "东印度公司成立", "事", "ship", 51.51, -0.08, "英国东印度公司获特许成立,逐步演变为统治印度的殖民强权。"),
|
||||||
|
("AD 1603", 1603, "日本 · 江户", "德川幕府建立", "事", "crown", 35.68, 139.76, "德川家康在关原之战后建立江户幕府,日本进入两百余年锁国与和平。"),
|
||||||
|
("AD 1605", 1605, "西班牙 · 马德里", "《堂吉诃德》", "物", "book", 40.42, -3.7, "塞万提斯发表《堂吉诃德》,被誉为欧洲第一部现代小说。"),
|
||||||
|
("AD 1609", 1609, "意大利 · 帕多瓦", "伽利略天文望远镜", "物", "atom", 45.41, 11.88, "伽利略以自制望远镜观测月球与木星卫星,用实证支持日心说。"),
|
||||||
|
("AD 1620", 1620, "北美 · 普利茅斯", "五月花号", "事", "ship", 41.96, -70.66, "清教徒乘“五月花号”抵达北美,签订《五月花号公约》,奠定自治传统。"),
|
||||||
|
("AD 1633", 1633, "意大利 · 罗马", "伽利略受审", "事", "atom", 41.9, 12.5, "宗教法庭审判伽利略,迫其放弃日心说,科学与教权的冲突达到顶点。"),
|
||||||
|
("AD 1642", 1642, "英格兰 · 伦敦", "英国内战爆发", "事", "sword", 51.51, -0.08, "议会与国王查理一世决裂,内战爆发,最终以君主被处决告终。"),
|
||||||
|
("AD 1644", 1644, "中国 · 北京", "清朝入关", "事", "crown", 39.9, 116.4, "清军入关定鼎北京,明朝灭亡,中国最后一个封建王朝开始统治。"),
|
||||||
|
("AD 1648", 1648, "欧洲 · 威斯特伐利亚", "《威斯特伐利亚和约》", "事", "scale", 51.96, 7.63, "三十年战争结束,确立主权国家与民族国家体系,是现代国际关系的起点。"),
|
||||||
|
("AD 1687", 1687, "英国 · 剑桥", "牛顿《自然哲学的数学原理》", "事", "apple", 52.2, 0.12, "牛顿提出三大运动定律与万有引力定律,奠定经典力学与现代科学的基础。"),
|
||||||
|
("AD 1689", 1689, "英格兰 · 伦敦", "《权利法案》", "物", "scale", 51.51, -0.08, "光荣革命后议会通过《权利法案》,确立君主立宪与议会主权。"),
|
||||||
|
("AD 1715", 1715, "法国 · 凡尔赛", "路易十四时代", "事", "crown", 48.8, 2.13, "“太阳王”路易十四的绝对君主制与凡尔赛宫,是欧洲专制的巅峰。"),
|
||||||
|
("AD 1735", 1735, "瑞典 · 乌普萨拉", "林奈分类法", "物", "tree", 59.86, 17.64, "林奈创立双名法与生物分类体系,为现代生物学奠定命名基础。"),
|
||||||
|
("AD 1750", 1750, "欧洲 · 英格兰", "工业革命", "事", "machine", 53.0, -2.0, "纺织机与蒸汽动力推动生产方式剧变,人类社会从农业转向工业。"),
|
||||||
|
("AD 1762", 1762, "法国 · 巴黎", "卢梭《社会契约论》", "物", "scroll", 48.85, 2.35, "卢梭提出人民主权与公意思想,深刻影响法国大革命与现代民主理论。"),
|
||||||
|
("AD 1769", 1769, "英国 · 格拉斯哥", "瓦特改良蒸汽机", "物", "machine", 55.86, -4.25, "瓦特改良的蒸汽机大幅提高热效率并输出旋转动力,成为工业革命核心引擎。"),
|
||||||
|
("AD 1770", 1770, "澳大利亚 · 植物学湾", "库克抵达澳大利亚", "事", "ship", -33.99, 151.2, "库克船长抵达澳洲东海岸并宣称为英国属地,欧洲人开始殖民澳洲。"),
|
||||||
|
("AD 1776", 1776, "北美 · 费城", "美国《独立宣言》", "事", "bell", 39.95, -75.16, "宣告“人人生而平等”,天赋人权与主权在民,开启现代共和制国家的先河。"),
|
||||||
|
("AD 1776", 1776, "英国 · 格拉斯哥", "亚当·斯密《国富论》", "物", "book", 55.86, -4.25, "亚当·斯密系统阐述自由市场与分工理论,奠定现代经济学。"),
|
||||||
|
("AD 1789", 1789, "法国 · 巴黎", "法国大革命", "事", "bell", 48.85, 2.35, "攻占巴士底狱点燃革命,自由平等博爱口号席卷欧洲,君主制被推翻。"),
|
||||||
|
("AD 1789", 1789, "美国 · 纽约", "美国宪法生效", "物", "scale", 40.71, -74.01, "美国联邦宪法生效,确立三权分立与联邦制,是成文宪法的典范。"),
|
||||||
|
("AD 1791", 1791, "法国 · 巴黎", "《人权宣言》", "物", "scale", 48.85, 2.35, "法国制宪会议颁布《人权与公民权宣言》,宣示天赋人权与法律面前人人平等。"),
|
||||||
|
("AD 1796", 1796, "英格兰 · 伯克利", "琴纳牛痘接种", "事", "apple", 51.69, -2.46, "爱德华·琴纳以牛痘接种预防天花,开创免疫学与疫苗时代。"),
|
||||||
|
("AD 1804", 1804, "法国 · 巴黎", "拿破仑称帝", "事", "crown", 48.85, 2.35, "拿破仑加冕为法兰西皇帝,颁布《拿破仑法典》,重塑欧洲版图。"),
|
||||||
|
("AD 1804", 1804, "海地 · 太子港", "海地独立", "事", "bell", 18.54, -72.34, "海地革命胜利,建立第一个黑人共和国,废除奴隶制。"),
|
||||||
|
("AD 1807", 1807, "英国 · 伦敦", "废除奴隶贸易", "事", "scale", 51.51, -0.08, "英国议会通过废除奴隶贸易法案,全球废奴运动由此推进。"),
|
||||||
|
("AD 1810", 1810, "拉丁美洲 · 委内瑞拉", "拉美独立运动", "事", "bell", 10.5, -66.9, "玻利瓦尔与圣马丁领导南美独立战争,西葡殖民帝国土崩瓦解。"),
|
||||||
|
("AD 1812", 1812, "俄国 · 莫斯科", "拿破仑远征俄国", "事", "sword", 55.75, 37.62, "拿破仑大军远征俄国失败,帝国由盛转衰,欧洲反法同盟反攻。"),
|
||||||
|
("AD 1815", 1815, "比利时 · 滑铁卢", "滑铁卢战役", "事", "sword", 50.68, 4.41, "反法同盟在滑铁卢最终击败拿破仑,欧洲进入维也纳体系时代。"),
|
||||||
|
("AD 1825", 1825, "英格兰 · 斯托克顿", "第一条铁路", "物", "machine", 54.57, -1.32, "斯托克顿至达灵顿铁路通车,蒸汽机车开启铁路时代。"),
|
||||||
|
("AD 1831", 1831, "英格兰 · 伦敦", "法拉第电磁感应", "事", "atom", 51.51, -0.08, "法拉第发现电磁感应,发电机的原理就此确立,电力时代曙光初现。"),
|
||||||
|
("AD 1839", 1839, "中国 · 广东虎门", "虎门销烟", "事", "flag", 22.8, 113.6, "林则徐在虎门销毁鸦片,引发第一次鸦片战争,中国近代史开端。"),
|
||||||
|
("AD 1840", 1840, "中国 · 广东", "第一次鸦片战争", "事", "sword", 22.8, 113.6, "英国以鸦片贸易为由发动战争,中国被迫打开国门,进入半殖民地时代。"),
|
||||||
|
("AD 1848", 1848, "欧洲 · 多国", "1848年革命", "事", "bell", 48.85, 2.35, "席卷欧洲的革命浪潮,推动民族主义与自由主义,虽败却埋下变革种子。"),
|
||||||
|
("AD 1848", 1848, "英格兰 · 伦敦", "《共产党宣言》", "物", "book", 51.51, -0.08, "马克思与恩格斯发表《共产党宣言》,科学社会主义与工人运动由此兴起。"),
|
||||||
|
("AD 1851", 1851, "英格兰 · 伦敦", "万国工业博览会", "事", "machine", 51.5, -0.17, "首届世界博览会在水晶宫举行,展示工业革命成果,是全球化早期象征。"),
|
||||||
|
("AD 1853", 1853, "日本 · 江户", "黑船来航", "事", "ship", 35.68, 139.76, "美国佩里舰队叩开日本国门,幕府锁国终结,明治维新由此酝酿。"),
|
||||||
|
("AD 1854", 1854, "克里米亚 · 塞瓦斯托波尔", "南丁格尔护理改革", "事", "flag", 44.6, 33.52, "南丁格尔在克里米亚战场改革战地护理,创立现代护理学。"),
|
||||||
|
("AD 1859", 1859, "英国 · 伦敦", "达尔文《物种起源》", "事", "tree", 51.5, -0.12, "达尔文提出以自然选择为核心的进化论,彻底改变人类对生命起源与演化的理解。"),
|
||||||
|
("AD 1861", 1861, "美国 · 萨姆特堡", "美国内战爆发", "事", "sword", 32.75, -79.87, "南北因奴隶制与州权决裂,内战爆发,最终废除奴隶制并维护统一。"),
|
||||||
|
("AD 1861", 1861, "意大利 · 都灵", "意大利统一", "事", "crown", 45.07, 7.68, "加富尔与加里波第促成意大利统一,意大利王国宣告成立。"),
|
||||||
|
("AD 1863", 1863, "美国 · 葛底斯堡", "《解放宣言》", "物", "scale", 39.83, -77.23, "林肯颁布《解放黑人奴隶宣言》,废奴成为内战目标,扭转战争性质。"),
|
||||||
|
("AD 1865", 1865, "美国 · 华盛顿", "林肯遇刺", "事", "flag", 38.9, -77.02, "林肯在剧院遇刺身亡,这位废奴总统成为美国统一与自由的象征。"),
|
||||||
|
("AD 1867", 1867, "瑞典 · 斯德哥尔摩", "诺贝尔炸药", "物", "machine", 59.33, 18.06, "诺贝尔发明安全炸药,推动采矿与工程,也奠定诺贝尔奖的财富基础。"),
|
||||||
|
("AD 1868", 1868, "日本 · 京都", "明治维新", "事", "crown", 35.01, 135.77, "日本推翻幕府、尊王攘夷,全面学习西方,迅速迈向近代化强国。"),
|
||||||
|
("AD 1869", 1869, "埃及 · 苏伊士", "苏伊士运河通航", "物", "ship", 30.5, 32.5, "连接地中海与红海的苏伊士运河开通,欧亚航运大为缩短。"),
|
||||||
|
("AD 1869", 1869, "俄国 · 圣彼得堡", "门捷列夫元素周期表", "物", "atom", 59.93, 30.36, "门捷列夫发表元素周期表,揭示化学元素的内在规律。"),
|
||||||
|
("AD 1871", 1871, "法国 · 凡尔赛", "德意志统一", "事", "crown", 48.8, 2.13, "俾斯麦促成德意志帝国在凡尔赛宫宣告成立,欧洲均势改写。"),
|
||||||
|
("AD 1871", 1871, "法国 · 巴黎", "巴黎公社", "事", "bell", 48.85, 2.35, "巴黎工人建立公社,虽仅存72天,却是无产阶级政权的首次尝试。"),
|
||||||
|
("AD 1876", 1876, "美国 · 波士顿", "电话发明", "物", "machine", 42.36, -71.06, "贝尔发明电话并成功通话,人类远程语音通信时代开启。"),
|
||||||
|
("AD 1879", 1879, "美国 · 门洛帕克", "爱迪生白炽灯", "物", "atom", 40.56, -74.33, "爱迪生改进白炽灯并建立供电系统,电灯走进千家万户。"),
|
||||||
|
("AD 1882", 1882, "德国 · 柏林", "科赫发现结核杆菌", "事", "apple", 52.52, 13.4, "罗伯特·科赫发现结核杆菌,奠定细菌学与传染病防治基础。"),
|
||||||
|
("AD 1885", 1885, "德国 · 曼海姆", "本茨汽车", "物", "machine", 49.49, 8.47, "卡尔·本茨制造第一辆内燃机汽车,汽车时代就此发端。"),
|
||||||
|
("AD 1885", 1885, "非洲 · 柏林", "柏林会议瓜分非洲", "事", "scale", 52.52, 13.4, "列强在柏林会议上瓜分非洲,非洲被殖民势力全面分割。"),
|
||||||
|
("AD 1889", 1889, "法国 · 巴黎", "埃菲尔铁塔", "物", "machine", 48.86, 2.29, "为世博会建造的埃菲尔铁塔落成,成为工业时代与巴黎的象征。"),
|
||||||
|
("AD 1895", 1895, "德国 · 维尔茨堡", "伦琴发现X射线", "事", "atom", 49.79, 9.95, "伦琴发现X射线,开创放射学,医疗影像与物理学进入新阶段。"),
|
||||||
|
("AD 1895", 1895, "法国 · 巴黎", "电影诞生", "物", "machine", 48.85, 2.35, "卢米埃尔兄弟首次公映电影,活动影像时代由此开启。"),
|
||||||
|
("AD 1896", 1896, "希腊 · 雅典", "现代奥运会", "事", "colosseum", 37.98, 23.73, "顾拜旦复兴奥运会,首届现代奥运会在雅典举行。"),
|
||||||
|
("AD 1898", 1898, "法国 · 巴黎", "居里夫人发现镭", "事", "atom", 48.85, 2.35, "居里夫妇发现钋与镭,开创放射性研究,居里夫人两获诺贝尔奖。"),
|
||||||
|
("AD 1900", 1900, "德国 · 柏林", "普朗克量子论", "事", "atom", 52.52, 13.4, "普朗克提出能量量子化假说,量子物理学由此诞生。"),
|
||||||
|
("AD 1901", 1901, "瑞典 · 斯德哥尔摩", "诺贝尔奖设立", "事", "book", 59.33, 18.06, "根据诺贝尔遗嘱设立,奖励物理、化学、医学、文学与和平领域杰出者。"),
|
||||||
|
("AD 1903", 1903, "美国 · 基蒂霍克", "莱特兄弟首飞", "物", "rocket", 36.06, -75.7, "莱特兄弟的“飞行者一号”实现首次动力飞行,人类翱翔蓝天梦想成真。"),
|
||||||
|
("AD 1905", 1905, "瑞士 · 伯尔尼", "爱因斯坦狭义相对论", "事", "atom", 46.95, 7.45, "爱因斯坦提出狭义相对论,得出质能方程 E=mc²,颠覆牛顿绝对时空观。"),
|
||||||
|
("AD 1905", 1905, "俄国 · 圣彼得堡", "1905年革命", "事", "bell", 59.93, 30.36, "俄国爆发大规模革命,迫使沙皇让步,是1917年革命的前奏。"),
|
||||||
|
("AD 1909", 1909, "美国 · 纽约", "福特T型车", "物", "machine", 42.33, -83.05, "福特T型车量产,流水线生产方式让汽车走进大众,工业进入大规模生产时代。"),
|
||||||
|
("AD 1911", 1911, "中国 · 武昌", "辛亥革命", "事", "bell", 30.58, 114.31, "武昌起义推翻清朝,结束两千余年帝制,中华民国成立。"),
|
||||||
|
("AD 1911", 1911, "挪威 · 南极", "阿蒙森抵达南极点", "事", "flag", -90.0, 0.0, "阿蒙森率队首次抵达南极点,极地探险时代的巅峰。"),
|
||||||
|
("AD 1912", 1912, "大西洋", "泰坦尼克号沉没", "事", "ship", 41.73, -49.95, "“不沉之船”泰坦尼克号首航撞冰山沉没,促使国际海事安全规则建立。"),
|
||||||
|
("AD 1914", 1914, "欧洲 · 萨拉热窝", "第一次世界大战", "事", "sword", 43.86, 18.41, "萨拉热窝刺杀点燃大战,同盟国与协约国厮杀四年,重绘世界版图。"),
|
||||||
|
("AD 1915", 1915, "德国 · 柏林", "爱因斯坦广义相对论", "事", "atom", 52.52, 13.4, "爱因斯坦完成广义相对论,将引力解释为时空弯曲,重塑宇宙观。"),
|
||||||
|
("AD 1917", 1917, "俄国 · 圣彼得堡", "俄国十月革命", "事", "bell", 59.93, 30.36, "布尔什维克夺取政权,建立世界上第一个社会主义国家,改变二十世纪进程。"),
|
||||||
|
("AD 1917", 1917, "美国 · 华盛顿", "美国参战", "事", "sword", 38.9, -77.02, "美国加入协约国参战,一战天平倾斜,威尔逊提出“十四点”。"),
|
||||||
|
("AD 1918", 1918, "欧洲 · 康边", "一战结束", "事", "scale", 49.42, 2.82, "德国签署停战协定,一战结束,四大帝国解体。"),
|
||||||
|
("AD 1918", 1918, "全球 · 多国", "西班牙流感", "事", "fossil", 40.4, -3.7, "西班牙流感全球大流行,感染数亿人,死亡数千万,是最致命的疫情之一。"),
|
||||||
|
("AD 1919", 1919, "法国 · 巴黎", "巴黎和会", "事", "scale", 48.85, 2.35, "战胜国召开巴黎和会,签订《凡尔赛和约》,埋下二战隐患。"),
|
||||||
|
("AD 1919", 1919, "中国 · 北京", "五四运动", "事", "bell", 39.9, 116.4, "北京学生抗议巴黎和会,新文化运动与民族觉醒汇流,中国近代史转折。"),
|
||||||
|
("AD 1920", 1920, "瑞士 · 日内瓦", "国际联盟成立", "事", "scale", 46.2, 6.15, "一战后成立的国际联盟,虽未能阻止二战,却是联合国的前身。"),
|
||||||
|
("AD 1922", 1922, "埃及 · 开罗", "图坦卡蒙墓发现", "物", "fossil", 25.74, 32.6, "卡特发现图坦卡蒙完整陵墓,掀起埃及学热潮,震惊世界。"),
|
||||||
|
("AD 1922", 1922, "苏维埃 · 莫斯科", "苏联成立", "事", "crown", 55.75, 37.62, "苏维埃社会主义共和国联盟成立,世界上第一个社会主义大国诞生。"),
|
||||||
|
("AD 1926", 1926, "英格兰 · 伦敦", "电视发明", "物", "machine", 51.51, -0.08, "贝尔德展示机械电视系统,电视时代萌芽,大众传媒革命开启。"),
|
||||||
|
("AD 1928", 1928, "苏格兰 · 伦敦", "弗莱明发现青霉素", "事", "apple", 51.51, -0.08, "弗莱明发现青霉素,抗生素时代开启,拯救亿万生命。"),
|
||||||
|
("AD 1929", 1929, "美国 · 纽约", "华尔街股灾", "事", "fossil", 40.71, -74.01, "纽约股市崩盘引发大萧条,全球经济陷入十年危机。"),
|
||||||
|
("AD 1931", 1931, "中国 · 沈阳", "九一八事变", "事", "sword", 41.8, 123.4, "日本关东军侵占东北,中国十四年抗战由此开始。"),
|
||||||
|
("AD 1933", 1933, "德国 · 柏林", "希特勒上台", "事", "crown", 52.52, 13.4, "希特勒就任德国总理,纳粹独裁建立,欧洲走向战争深渊。"),
|
||||||
|
("AD 1934", 1934, "中国 · 江西", "红军长征", "事", "flag", 26.0, 115.0, "中央红军开始长征,历经两万五千里,中国革命转危为安。"),
|
||||||
|
("AD 1936", 1936, "西班牙 · 马德里", "西班牙内战", "事", "sword", 40.42, -3.7, "西班牙内战爆发,成为二战前意识形态较量的预演。"),
|
||||||
|
("AD 1937", 1937, "中国 · 北京", "七七事变", "事", "sword", 39.9, 116.4, "卢沟桥事变爆发,中国全民族抗战开始,二战东方战场形成。"),
|
||||||
|
("AD 1937", 1937, "中国 · 南京", "南京大屠杀", "事", "flag", 32.06, 118.8, "日军侵占南京,制造惨绝人寰的大屠杀,三十万同胞遇难。"),
|
||||||
|
("AD 1939", 1939, "欧洲 · 波兰", "第二次世界大战", "事", "sword", 52.23, 21.01, "德国闪击波兰,二战全面爆发,人类陷入空前浩劫。"),
|
||||||
|
("AD 1940", 1940, "英国 · 伦敦", "不列颠空战", "事", "rocket", 51.51, -0.08, "英国皇家空军挫败德国空袭,保卫英伦,挫败纳粹入侵企图。"),
|
||||||
|
("AD 1941", 1941, "美国 · 珍珠港", "珍珠港事件", "事", "ship", 21.36, -157.95, "日本偷袭珍珠港,美国参战,二战扩展为全球战争。"),
|
||||||
|
("AD 1942", 1942, "美国 · 芝加哥", "曼哈顿计划", "事", "atom", 41.88, -87.63, "美国启动原子弹研制计划,核时代的大幕就此拉开。"),
|
||||||
|
("AD 1943", 1943, "苏联 · 斯大林格勒", "斯大林格勒战役", "事", "sword", 48.7, 44.5, "苏军在斯大林格勒扭转战局,成为二战东线的转折点。"),
|
||||||
|
("AD 1944", 1944, "法国 · 诺曼底", "诺曼底登陆", "事", "sword", 49.4, -0.6, "盟军诺曼底登陆开辟第二战场,加速纳粹德国覆灭。"),
|
||||||
|
("AD 1945", 1945, "德国 · 柏林", "二战欧洲结束", "事", "scale", 52.52, 13.4, "德国无条件投降,欧洲战事结束,纳粹第三帝国覆灭。"),
|
||||||
|
("AD 1945", 1945, "世界 · 纽约", "联合国成立", "事", "dove", 40.75, -73.97, "51个国家签署《联合国宪章》,以维护世界和平、促进合作为宗旨的全球组织诞生。"),
|
||||||
|
("AD 1945", 1945, "日本 · 广岛", "原子弹爆炸", "事", "atom", 34.39, 132.46, "美国在广岛、长崎投下原子弹,日本投降,核武器首次用于实战。"),
|
||||||
|
("AD 1946", 1946, "美国 · 费城", "第一台电子计算机", "物", "machine", 39.95, -75.16, "ENIAC电子计算机问世,开启计算机与信息时代。"),
|
||||||
|
("AD 1947", 1947, "印度 · 新德里", "印度独立", "事", "bell", 28.61, 77.21, "印度独立并印巴分治,英国殖民帝国开始解体。"),
|
||||||
|
("AD 1947", 1947, "美国 · 新墨西哥", "晶体管发明", "物", "machine", 32.8, -106.0, "贝尔实验室发明晶体管,电子设备小型化与数字化时代来临。"),
|
||||||
|
("AD 1948", 1948, "以色列 · 特拉维夫", "以色列建国", "事", "crown", 32.08, 34.78, "以色列宣告独立,中东格局剧变,阿以冲突绵延。"),
|
||||||
|
("AD 1948", 1948, "世界 · 日内瓦", "《世界人权宣言》", "物", "scale", 46.2, 6.15, "联合国通过《世界人权宣言》,确立普世人权标准。"),
|
||||||
|
("AD 1949", 1949, "中国 · 北京", "中华人民共和国成立", "事", "crown", 39.9, 116.4, "中华人民共和国成立,中国人民从此站起来了,开启民族复兴新纪元。"),
|
||||||
|
("AD 1949", 1949, "欧洲 · 布鲁塞尔", "北约成立", "事", "scale", 50.85, 4.35, "北大西洋公约组织成立,冷战阵营对立的军事同盟形成。"),
|
||||||
|
("AD 1950", 1950, "朝鲜半岛", "朝鲜战争", "事", "sword", 38.0, 127.0, "朝鲜战争爆发,冷战首次热战,东亚格局固化。"),
|
||||||
|
("AD 1951", 1951, "法国 · 巴黎", "欧洲煤钢共同体", "事", "scale", 48.85, 2.35, "法德等六国建立煤钢共同体,是欧盟一体化的起点。"),
|
||||||
|
("AD 1953", 1953, "英格兰 · 剑桥", "DNA双螺旋", "事", "tree", 52.2, 0.12, "沃森与克里克提出DNA双螺旋结构,分子生物学革命开启。"),
|
||||||
|
("AD 1953", 1953, "尼泊尔 · 珠峰", "首次登顶珠峰", "事", "flag", 27.99, 86.93, "希拉里与丹增首次登顶珠穆朗玛峰,人类征服地球之巅。"),
|
||||||
|
("AD 1955", 1955, "印度尼西亚 · 万隆", "万隆会议", "事", "dove", -6.9, 107.6, "亚非29国召开万隆会议,不结盟运动与第三世界力量兴起。"),
|
||||||
|
("AD 1957", 1957, "苏联 · 拜科努尔", "斯普特尼克1号", "物", "rocket", 45.96, 63.32, "苏联发射第一颗人造卫星,太空时代与美苏太空竞赛开启。"),
|
||||||
|
("AD 1958", 1958, "美国 · 华盛顿", "NASA成立", "事", "rocket", 38.9, -77.02, "美国国家航空航天局成立,统筹美国航天事业。"),
|
||||||
|
("AD 1959", 1959, "古巴 · 哈瓦那", "古巴革命", "事", "bell", 23.11, -82.37, "卡斯特罗领导古巴革命成功,拉美左翼浪潮兴起。"),
|
||||||
|
("AD 1961", 1961, "苏联 · 拜科努尔", "加加林进入太空", "事", "rocket", 45.96, 63.32, "加加林乘“东方一号”绕地球飞行,成为首位进入太空的人类。"),
|
||||||
|
("AD 1961", 1961, "德国 · 柏林", "柏林墙", "物", "scale", 52.52, 13.4, "东德修建柏林墙,冷战的铁幕具象化为水泥墙,分裂德国28年。"),
|
||||||
|
("AD 1962", 1962, "古巴 · 加勒比", "古巴导弹危机", "事", "atom", 23.0, -80.0, "美苏在古巴导弹危机中濒临核战边缘,冷战的至暗时刻。"),
|
||||||
|
("AD 1963", 1963, "美国 · 华盛顿", "马丁·路德·金演讲", "事", "dove", 38.89, -77.04, "马丁·路德·金发表《我有一个梦想》,民权运动达到高潮。"),
|
||||||
|
("AD 1964", 1964, "中国 · 罗布泊", "中国第一颗原子弹", "事", "atom", 40.5, 90.0, "中国成功试爆第一颗原子弹,成为核大国,打破核垄断。"),
|
||||||
|
("AD 1965", 1965, "美国 · 越南", "越南战争升级", "事", "sword", 14.0, 108.0, "美国大规模卷入越南战争,冷战在东南亚激烈较量。"),
|
||||||
|
("AD 1966", 1966, "中国 · 北京", "文化大革命", "事", "flag", 39.9, 116.4, "十年浩劫席卷中国,社会动荡,给国家带来严重挫折。"),
|
||||||
|
("AD 1967", 1967, "南非 · 开普敦", "首例心脏移植", "事", "apple", -33.92, 18.42, "巴纳德完成世界首例心脏移植手术,器官移植进入新纪元。"),
|
||||||
|
("AD 1968", 1968, "法国 · 巴黎", "五月风暴", "事", "bell", 48.85, 2.35, "法国学生与工人掀起五月风暴,冲击传统秩序,影响深远。"),
|
||||||
|
("AD 1969", 1969, "美国 · 卡纳维拉尔角", "阿波罗11号登月", "事", "rocket", 28.4, -80.6, "阿波罗11号将人类首次送上月球,阿姆斯特朗迈出“人类一大步”。"),
|
||||||
|
("AD 1969", 1969, "美国 · 洛杉矶", "互联网诞生", "事", "machine", 34.05, -118.24, "ARPANET前身节点联通,互联网的雏形诞生,信息时代来临。"),
|
||||||
|
("AD 1971", 1971, "中国 · 北京", "中国恢复联合国席位", "事", "dove", 40.75, -73.97, "联合国大会通过2758号决议,恢复中华人民共和国合法席位。"),
|
||||||
|
("AD 1972", 1972, "中国 · 北京", "尼克松访华", "事", "dove", 39.9, 116.4, "尼克松访华,中美关系破冰,改变冷战格局与东亚秩序。"),
|
||||||
|
("AD 1973", 1973, "中东 · 埃及叙利亚", "第四次中东战争", "事", "sword", 30.0, 33.0, "埃及叙利亚突袭以色列,引发石油危机,中东格局再变。"),
|
||||||
|
("AD 1975", 1975, "美国 · 西雅图", "微软成立", "事", "machine", 47.61, -122.33, "比尔·盖茨与保罗·艾伦创立微软,个人计算机软件时代开启。"),
|
||||||
|
("AD 1976", 1976, "美国 · 库比蒂诺", "苹果公司成立", "事", "apple", 37.33, -122.03, "乔布斯与沃兹尼亚克创立苹果公司,个人电脑革命由此展开。"),
|
||||||
|
("AD 1978", 1978, "中国 · 北京", "改革开放", "事", "scale", 39.9, 116.4, "十一届三中全会决定改革开放,中国从此走上经济腾飞之路。"),
|
||||||
|
("AD 1979", 1979, "伊朗 · 德黑兰", "伊朗伊斯兰革命", "事", "crown", 35.69, 51.39, "霍梅尼领导伊斯兰革命推翻巴列维王朝,中东政治宗教化。"),
|
||||||
|
("AD 1980", 1980, "意大利 · 罗马", "世界卫生组织消灭天花", "事", "apple", 41.9, 12.5, "世卫组织宣布天花被根除,人类首次彻底消灭一种传染病。"),
|
||||||
|
("AD 1981", 1981, "美国 · 纽约", "IBM个人电脑", "物", "machine", 40.71, -74.01, "IBM推出5150个人电脑,PC标准确立,微机普及时代到来。"),
|
||||||
|
("AD 1985", 1985, "苏联 · 莫斯科", "戈尔巴乔夫改革", "事", "scale", 55.75, 37.62, "戈尔巴乔夫推行改革与新思维,苏联进入变革与动荡期。"),
|
||||||
|
("AD 1986", 1986, "苏联 · 切尔诺贝利", "切尔诺贝利核事故", "事", "atom", 51.39, 30.1, "切尔诺贝利核电站爆炸,最严重的核灾难,促使全球反思核安全。"),
|
||||||
|
("AD 1989", 1989, "德国 · 柏林", "柏林墙倒塌", "事", "bell", 52.52, 13.4, "柏林墙倒塌,德国统一进程启动,冷战走向终结。"),
|
||||||
|
("AD 1989", 1989, "世界 · 日内瓦", "万维网发明", "物", "machine", 46.2, 6.15, "伯纳斯-李提出万维网,互联网进入大众视野,信息革命加速。"),
|
||||||
|
("AD 1990", 1990, "德国 · 柏林", "德国统一", "事", "crown", 52.52, 13.4, "两德统一,冷战分裂的象征终结。"),
|
||||||
|
("AD 1990", 1990, "美国 · 佛罗里达", "哈勃望远镜", "物", "rocket", 28.4, -80.6, "哈勃太空望远镜发射升空,人类对宇宙的观测进入新纪元。"),
|
||||||
|
("AD 1991", 1991, "苏联 · 莫斯科", "苏联解体", "事", "scale", 55.75, 37.62, "苏联正式解体,冷战结束,世界进入单极与全球化时代。"),
|
||||||
|
("AD 1993", 1993, "欧洲 · 马斯特里赫特", "欧盟成立", "事", "scale", 50.85, 5.68, "《马斯特里赫特条约》生效,欧洲联盟正式诞生。"),
|
||||||
|
("AD 1994", 1994, "南非 · 比勒陀利亚", "南非结束种族隔离", "事", "dove", -25.75, 28.19, "曼德拉当选总统,南非废除种族隔离制度,实现种族和解。"),
|
||||||
|
("AD 1995", 1995, "世界 · 日内瓦", "世界贸易组织成立", "事", "scale", 46.2, 6.15, "WTO成立,全球多边贸易体系制度化,全球化加速。"),
|
||||||
|
("AD 1996", 1996, "苏格兰 · 爱丁堡", "克隆羊多利", "事", "tree", 55.95, -3.19, "克隆羊多利诞生,体细胞克隆技术震惊世界,引发伦理大讨论。"),
|
||||||
|
("AD 1997", 1997, "中国 · 香港", "香港回归", "事", "crown", 22.32, 114.17, "香港回归祖国,一国两制付诸实践。"),
|
||||||
|
("AD 1998", 1998, "美国 · 加利福尼亚", "谷歌成立", "事", "machine", 37.42, -122.08, "拉里·佩奇与谢尔盖·布林创立谷歌,搜索引擎重塑信息获取方式。"),
|
||||||
|
("AD 1999", 1999, "欧洲 · 法兰克福", "欧元启动", "事", "scale", 50.11, 8.68, "欧元正式启动,欧洲货币一体化迈出历史性一步。"),
|
||||||
|
("AD 1999", 1999, "中国 · 澳门", "澳门回归", "事", "crown", 22.2, 113.55, "澳门回归祖国,殖民时代在亚洲落幕。"),
|
||||||
|
("AD 2000", 2000, "世界 · 多国", "人类基因组草图", "事", "tree", 38.9, -77.02, "人类基因组计划公布工作草图,生命科学进入基因组时代。"),
|
||||||
|
("AD 2001", 2001, "美国 · 纽约", "9·11事件", "事", "flag", 40.71, -74.01, "恐怖分子劫机袭击世贸中心与五角大楼,全球反恐战争开启。"),
|
||||||
|
("AD 2001", 2001, "中国 · 北京", "中国加入世贸组织", "事", "scale", 39.9, 116.4, "中国加入WTO,深度融入全球经济体系,经济高速增长。"),
|
||||||
|
("AD 2003", 2003, "伊拉克 · 巴格达", "伊拉克战争", "事", "sword", 33.31, 44.36, "美英联军发动伊拉克战争,中东秩序再度动荡。"),
|
||||||
|
("AD 2003", 2003, "中国 · 酒泉", "神舟五号载人航天", "事", "rocket", 40.96, 100.29, "杨利伟乘神舟五号进入太空,中国成为第三个独立载人航天国家。"),
|
||||||
|
("AD 2004", 2004, "美国 · 门洛帕克", "脸书成立", "事", "machine", 37.44, -122.14, "扎克伯格创立Facebook,社交媒体时代全面开启。"),
|
||||||
|
("AD 2007", 2007, "美国 · 旧金山", "iPhone发布", "物", "machine", 37.77, -122.42, "苹果发布iPhone,智能手机与移动互联网时代由此开启。"),
|
||||||
|
("AD 2008", 2008, "中国 · 北京", "北京奥运会", "事", "colosseum", 39.9, 116.4, "北京举办第29届夏季奥运会,中国以开放姿态亮相世界。"),
|
||||||
|
("AD 2008", 2008, "美国 · 纽约", "全球金融危机", "事", "fossil", 40.71, -74.01, "雷曼兄弟倒闭引发全球金融危机,世界经济陷入衰退。"),
|
||||||
|
("AD 2012", 2012, "瑞士 · 日内瓦", "希格斯玻色子发现", "事", "atom", 46.23, 6.06, "CERN发现希格斯玻色子,标准模型最后一块拼图就位。"),
|
||||||
|
("AD 2015", 2015, "法国 · 巴黎", "《巴黎协定》", "物", "scale", 48.85, 2.35, "近200国通过《巴黎协定》,共同应对气候变化。"),
|
||||||
|
("AD 2016", 2016, "美国 · 华盛顿", "引力波首次探测", "事", "atom", 38.89, -77.04, "LIGO首次直接探测到引力波,证实爱因斯坦百年预言。"),
|
||||||
|
("AD 2019", 2019, "中国 · 武汉", "新冠疫情", "事", "fossil", 30.59, 114.3, "新冠疫情全球大流行,深刻改变世界公共卫生与经济秩序。"),
|
||||||
|
("AD 2020", 2020, "美国 · 卡纳维拉尔角", "SpaceX载人航天", "事", "rocket", 28.56, -80.57, "SpaceX龙飞船首次商业载人飞行,商业航天时代开启。"),
|
||||||
|
("AD 2021", 2021, "中国 · 文昌", "天问一号登陆火星", "事", "rocket", 19.6, 110.9, "中国天问一号携祝融号成功着陆火星,深空探测迈入新阶段。"),
|
||||||
|
("AD 2022", 2022, "美国 · 华盛顿", "韦伯望远镜", "物", "rocket", 38.9, -77.02, "詹姆斯·韦伯太空望远镜发布首批深空影像,宇宙观测能力飞跃。"),
|
||||||
|
]
|
||||||
|
|
||||||
|
assert len(DATA) == 300, f"原始数据应为 300 条,当前 {len(DATA)} 条"
|
||||||
|
|
||||||
|
# —— 坐标去重:相距 DEDUP_KM 以内视为同一地点,每组保留一条 ——
|
||||||
|
DEDUP_KM = 10.0
|
||||||
|
|
||||||
|
# 保留优先级:原 20 条头条词条优先保留(其余组保留最早一条)
|
||||||
|
PRIORITY = {
|
||||||
|
'露西 · 南方古猿', '楔形文字', '胡夫金字塔', '孔子', '雅典卫城 · 帕特农神庙',
|
||||||
|
'秦始皇统一六国', '亚历山大大帝', '罗马斗兽场', '蔡伦造纸', '穆罕默德',
|
||||||
|
'成吉思汗', '哥伦布发现新大陆', '麦哲伦环球航行', '牛顿《自然哲学的数学原理》',
|
||||||
|
'瓦特改良蒸汽机', '美国《独立宣言》', '达尔文《物种起源》', '爱因斯坦狭义相对论',
|
||||||
|
'联合国成立', '阿波罗11号登月',
|
||||||
|
}
|
||||||
|
|
||||||
|
def _hav(a, b):
|
||||||
|
R = 6371.0
|
||||||
|
dlat = math.radians(b[6] - a[6])
|
||||||
|
dlng = math.radians(b[7] - a[7])
|
||||||
|
la = math.radians(a[6]); lb = math.radians(b[6])
|
||||||
|
h = math.sin(dlat / 2) ** 2 + math.cos(la) * math.cos(lb) * math.sin(dlng / 2) ** 2
|
||||||
|
return 2 * R * math.asin(math.sqrt(h))
|
||||||
|
|
||||||
|
def dedupe(entries, km=DEDUP_KM):
|
||||||
|
n = len(entries)
|
||||||
|
used = [False] * n
|
||||||
|
out = []
|
||||||
|
for i in range(n):
|
||||||
|
if used[i]:
|
||||||
|
continue
|
||||||
|
cluster = [i]
|
||||||
|
used[i] = True
|
||||||
|
changed = True
|
||||||
|
while changed:
|
||||||
|
changed = False
|
||||||
|
for j in range(n):
|
||||||
|
if used[j]:
|
||||||
|
continue
|
||||||
|
if any(_hav(entries[k], entries[j]) <= km for k in cluster):
|
||||||
|
cluster.append(j)
|
||||||
|
used[j] = True
|
||||||
|
changed = True
|
||||||
|
members = [entries[k] for k in cluster]
|
||||||
|
pick = None
|
||||||
|
for m in members:
|
||||||
|
if m[3] in PRIORITY:
|
||||||
|
pick = m
|
||||||
|
break
|
||||||
|
out.append(pick if pick else members[0])
|
||||||
|
return out
|
||||||
|
|
||||||
|
DEDUPED = dedupe(DATA)
|
||||||
|
print(f"坐标去重:{len(DATA)} -> {len(DEDUPED)} 条(阈值 {DEDUP_KM} km)")
|
||||||
|
|
||||||
|
def esc(s):
|
||||||
|
return s.replace("\\", "\\\\").replace("'", "\\'")
|
||||||
|
|
||||||
|
def normalize_time(time, tn):
|
||||||
|
"""统一时间格式:约数用 ~,公元前后用 BC/AD 缩写;世纪与区间原样保留。"""
|
||||||
|
if '—' in time or 'century' in time:
|
||||||
|
return time
|
||||||
|
approx = time.lstrip().startswith('~')
|
||||||
|
is_bc = 'BC' in time
|
||||||
|
a = abs(tn)
|
||||||
|
if approx:
|
||||||
|
if is_bc:
|
||||||
|
if a >= 1000000:
|
||||||
|
return f"~ BC {a:,}"
|
||||||
|
if a >= 10000:
|
||||||
|
return f"~{a:,} BC"
|
||||||
|
return f"~{a} BC"
|
||||||
|
return f"~AD {a}"
|
||||||
|
if is_bc:
|
||||||
|
return f"{a:,} BC" if a >= 10000 else f"{a} BC"
|
||||||
|
return f"AD {a:,}" if a >= 10000 else f"AD {a}"
|
||||||
|
|
||||||
|
lines = ["// 世界历史里程碑条目数据",
|
||||||
|
"// timeNumeric 用于时间轴排序/定位(公元前为负,公元后为正,单位:年)",
|
||||||
|
"",
|
||||||
|
"export const CATEGORY_INFO = {",
|
||||||
|
" 人: { color: '#a03a2a', en: 'PERSONA' },",
|
||||||
|
" 事: { color: '#2c4a6b', en: 'EVENTUS' },",
|
||||||
|
" 物: { color: '#3f5c3a', en: 'ARTIFEX' },",
|
||||||
|
"};",
|
||||||
|
"",
|
||||||
|
"export const ENTRIES = ["]
|
||||||
|
for i, (time, tn, region, name, cat, sketch, lat, lng, desc) in enumerate(DEDUPED, 1):
|
||||||
|
lines.append(" {")
|
||||||
|
lines.append(f" id: 'No.{i:03d}',")
|
||||||
|
lines.append(f" time: '{esc(normalize_time(time, tn))}',")
|
||||||
|
lines.append(f" timeNumeric: {tn},")
|
||||||
|
lines.append(f" region: '{esc(region)}',")
|
||||||
|
lines.append(f" name: '{esc(name)}',")
|
||||||
|
lines.append(f" category: '{esc(cat)}',")
|
||||||
|
lines.append(f" sketch: '{esc(sketch)}',")
|
||||||
|
lines.append(f" lat: {lat}, lng: {lng},")
|
||||||
|
lines.append(f" description: '{esc(desc)}',")
|
||||||
|
lines.append(" },")
|
||||||
|
lines.append("];")
|
||||||
|
lines.append("")
|
||||||
|
lines.append("// 时间轴横向定位:对称对数映射,远古压缩、近代展开")
|
||||||
|
lines.append("export function timelineX(t) {")
|
||||||
|
lines.append(" const sign = t >= 0 ? 1 : -1;")
|
||||||
|
lines.append(" const mag = Math.log10(1 + Math.abs(t) / 50);")
|
||||||
|
lines.append(" return sign * mag * 1.15;")
|
||||||
|
lines.append("}")
|
||||||
|
|
||||||
|
with open("src/data/entries.js", "w", encoding="utf-8") as f:
|
||||||
|
f.write("\n".join(lines) + "\n")
|
||||||
|
|
||||||
|
print("written entries.js with", len(DEDUPED), "entries")
|
||||||
+8
-3
@@ -9,7 +9,7 @@ import { clamp, easeInOutCubic } from '../utils/easing.js';
|
|||||||
|
|
||||||
const CAMERA_POSES = {
|
const CAMERA_POSES = {
|
||||||
binder: { pos: new THREE.Vector3(0.2, 0.75, 2.95), target: new THREE.Vector3(0, 0.02, 0), max: 4.4, min: 2.4 },
|
binder: { pos: new THREE.Vector3(0.2, 0.75, 2.95), target: new THREE.Vector3(0, 0.02, 0), max: 4.4, min: 2.4 },
|
||||||
timeline: { pos: new THREE.Vector3(0, 1.4, 6.86), target: new THREE.Vector3(0, 0, 0), max: 8, min: 6 },
|
timeline: { pos: new THREE.Vector3(0, 1.4, 6.86), target: new THREE.Vector3(0, 0, 0), max: 8, min: 1.2 },
|
||||||
globe: { pos: new THREE.Vector3(0, 0.35, 3.7), target: new THREE.Vector3(0, 0, 0), max: 8, min: 2.2 },
|
globe: { pos: new THREE.Vector3(0, 0.35, 3.7), target: new THREE.Vector3(0, 0, 0), max: 8, min: 2.2 },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ export class App {
|
|||||||
this.interactives.binder.push(m);
|
this.interactives.binder.push(m);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.timeline.cards.forEach((c) => this.interactives.timeline.push(c.card));
|
this.timeline.cards.forEach((c) => this.interactives.timeline.push(c.front, c.back));
|
||||||
// 地理视图由 Cesium 自行拾取,不参与 Three.js 射线检测
|
// 地理视图由 Cesium 自行拾取,不参与 Three.js 射线检测
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +291,10 @@ export class App {
|
|||||||
document.getElementById('detail-card').classList.remove('hidden');
|
document.getElementById('detail-card').classList.remove('hidden');
|
||||||
this.binder.syncTo(index);
|
this.binder.syncTo(index);
|
||||||
// 时间轴:点击后滑动使该卡片居中
|
// 时间轴:点击后滑动使该卡片居中
|
||||||
if (view === 'timeline') this._centerTimeline(index);
|
if (view === 'timeline') {
|
||||||
|
this._centerTimeline(index);
|
||||||
|
this.timeline.flip(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,6 +316,7 @@ export class App {
|
|||||||
this.overlay.setEntry(entry, this.selected, ENTRIES.length);
|
this.overlay.setEntry(entry, this.selected, ENTRIES.length);
|
||||||
this.timeline.setSelected(this.selected);
|
this.timeline.setSelected(this.selected);
|
||||||
this.globe.setSelected(this.selected);
|
this.globe.setSelected(this.selected);
|
||||||
|
if (this.view === 'globe') this.globe.flyToEntry(this.selected); // 地球页切换时自动居中
|
||||||
if (syncBinder) this.binder.syncTo(this.selected);
|
if (syncBinder) this.binder.syncTo(this.selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,6 +442,7 @@ export class App {
|
|||||||
const dt = Math.min(this.clock.getDelta(), 0.05);
|
const dt = Math.min(this.clock.getDelta(), 0.05);
|
||||||
|
|
||||||
this.binder.update(dt);
|
this.binder.update(dt);
|
||||||
|
this.timeline.update(dt);
|
||||||
|
|
||||||
if (this.cameraTween) {
|
if (this.cameraTween) {
|
||||||
const t = this.cameraTween;
|
const t = this.cameraTween;
|
||||||
|
|||||||
+1874
-92
File diff suppressed because it is too large
Load Diff
+210
-21
@@ -1,5 +1,5 @@
|
|||||||
import * as Cesium from 'cesium';
|
import * as Cesium from 'cesium';
|
||||||
import { ENTRIES, CATEGORY_INFO } from '../data/entries.js';
|
import { ENTRIES } from '../data/entries.js';
|
||||||
|
|
||||||
// 免费、无需 Ion 令牌的影像与地形源
|
// 免费、无需 Ion 令牌的影像与地形源
|
||||||
const IMAGERY_URL =
|
const IMAGERY_URL =
|
||||||
@@ -7,8 +7,59 @@ const IMAGERY_URL =
|
|||||||
const TERRAIN_URL =
|
const TERRAIN_URL =
|
||||||
'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer';
|
'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer';
|
||||||
|
|
||||||
// 点击标记后相机飞到的目标高度(米,约 1500 km 区域尺度)
|
// 点击标记后飞到的目标高度(米,与初始视图一致,保证左键拖拽始终是“整球旋转”)
|
||||||
const FLY_TO_HEIGHT = 1.5e6;
|
const FLY_TO_HEIGHT = 7.2e6;
|
||||||
|
|
||||||
|
// 计算多边形集合的球面面积(km²),用于按国土面积分级国家名字号
|
||||||
|
function sphericalAreaKm2(polys) {
|
||||||
|
let area = 0;
|
||||||
|
for (let p = 0; p < polys.length; p++) {
|
||||||
|
const rings = polys[p];
|
||||||
|
for (let r = 0; r < rings.length; r++) {
|
||||||
|
const ring = rings[r];
|
||||||
|
let s = 0;
|
||||||
|
for (let i = 0; i < ring.length; i++) {
|
||||||
|
const [lng1, lat1] = ring[i];
|
||||||
|
const [lng2, lat2] = ring[(i + 1) % ring.length];
|
||||||
|
let dlng = ((lng2 - lng1) * Math.PI) / 180;
|
||||||
|
while (dlng > Math.PI) dlng -= 2 * Math.PI;
|
||||||
|
while (dlng < -Math.PI) dlng += 2 * Math.PI;
|
||||||
|
const f1 = (lat1 * Math.PI) / 180;
|
||||||
|
const f2 = (lat2 * Math.PI) / 180;
|
||||||
|
s += dlng * (2 + Math.sin(f1) + Math.sin(f2));
|
||||||
|
}
|
||||||
|
const ringArea = Math.abs(s) / 2;
|
||||||
|
area += (r === 0 ? 1 : -1) * ringArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const R = 6371; // km
|
||||||
|
return area * R * R;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成圆角黑色背景 canvas(尺寸按文字实际测量宽度)
|
||||||
|
function makeRoundedBg(text, fontSize) {
|
||||||
|
const padX = 12, padY = 7, radius = 10;
|
||||||
|
const m = document.createElement('canvas');
|
||||||
|
const mctx = m.getContext('2d');
|
||||||
|
mctx.font = `${fontSize}px "Noto Serif SC", "Songti SC", "STSong", "SimSun", Georgia, serif`;
|
||||||
|
const textW = mctx.measureText(text).width;
|
||||||
|
const w = Math.max(1, Math.ceil(textW + padX * 2));
|
||||||
|
const h = Math.ceil(fontSize + padY * 2);
|
||||||
|
const c = document.createElement('canvas');
|
||||||
|
c.width = w;
|
||||||
|
c.height = h;
|
||||||
|
const ctx = c.getContext('2d');
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(radius, 0);
|
||||||
|
ctx.arcTo(w, 0, w, h, radius);
|
||||||
|
ctx.arcTo(w, h, 0, h, radius);
|
||||||
|
ctx.arcTo(0, h, 0, 0, radius);
|
||||||
|
ctx.arcTo(0, 0, w, 0, radius);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fillStyle = 'rgba(20, 16, 13, 0.88)';
|
||||||
|
ctx.fill();
|
||||||
|
return { canvas: c, textW };
|
||||||
|
}
|
||||||
|
|
||||||
export class CesiumGlobe {
|
export class CesiumGlobe {
|
||||||
constructor({ onPick } = {}) {
|
constructor({ onPick } = {}) {
|
||||||
@@ -61,6 +112,7 @@ export class CesiumGlobe {
|
|||||||
controller.maximumZoomDistance = 8e6; // 最远约 8000 km
|
controller.maximumZoomDistance = 8e6; // 最远约 8000 km
|
||||||
|
|
||||||
// 限制鼠标按钮:只保留左键旋转 + 滚轮缩放;禁用中键倾斜、右键缩放
|
// 限制鼠标按钮:只保留左键旋转 + 滚轮缩放;禁用中键倾斜、右键缩放
|
||||||
|
controller.rotateEventTypes = Cesium.CameraEventType.LEFT_DRAG;
|
||||||
controller.zoomEventTypes = [
|
controller.zoomEventTypes = [
|
||||||
Cesium.CameraEventType.WHEEL,
|
Cesium.CameraEventType.WHEEL,
|
||||||
Cesium.CameraEventType.PINCH,
|
Cesium.CameraEventType.PINCH,
|
||||||
@@ -104,6 +156,8 @@ export class CesiumGlobe {
|
|||||||
|
|
||||||
this._buildMarkers();
|
this._buildMarkers();
|
||||||
this._setupPick();
|
this._setupPick();
|
||||||
|
this._loadBoundaries();
|
||||||
|
this._buildCityLabels();
|
||||||
|
|
||||||
viewer.camera.setView({
|
viewer.camera.setView({
|
||||||
destination: Cesium.Cartesian3.fromDegrees(35, 12, 7.2e6),
|
destination: Cesium.Cartesian3.fromDegrees(35, 12, 7.2e6),
|
||||||
@@ -117,40 +171,64 @@ export class CesiumGlobe {
|
|||||||
const ink = Cesium.Color.fromCssColorString('#14100d');
|
const ink = Cesium.Color.fromCssColorString('#14100d');
|
||||||
|
|
||||||
ENTRIES.forEach((entry, i) => {
|
ENTRIES.forEach((entry, i) => {
|
||||||
const info = CATEGORY_INFO[entry.category] || CATEGORY_INFO['事'];
|
|
||||||
const color = Cesium.Color.fromCssColorString(info.color);
|
|
||||||
|
|
||||||
const entity = viewer.entities.add({
|
const entity = viewer.entities.add({
|
||||||
position: Cesium.Cartesian3.fromDegrees(entry.lng, entry.lat),
|
position: Cesium.Cartesian3.fromDegrees(entry.lng, entry.lat),
|
||||||
point: {
|
point: {
|
||||||
pixelSize: 8,
|
pixelSize: 10, // 标记宽一点
|
||||||
color,
|
color: gold, // 金色圆形
|
||||||
outlineColor: gold,
|
outlineColor: ink, // 外侧 1px 黑边
|
||||||
outlineWidth: 1.5,
|
outlineWidth: 1,
|
||||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
|
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 1e7),
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
text: entry.name,
|
text: entry.name,
|
||||||
font: '17px Georgia, "Noto Serif SC", "Songti SC", "Microsoft YaHei", serif',
|
font: '19px "Noto Serif SC", "Songti SC", "STSong", "SimSun", Georgia, serif',
|
||||||
fillColor: ivory,
|
fillColor: gold,
|
||||||
outlineColor: ink,
|
outlineColor: ink,
|
||||||
outlineWidth: 3,
|
outlineWidth: 3,
|
||||||
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
||||||
pixelOffset: new Cesium.Cartesian2(0, -16),
|
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
||||||
|
verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
||||||
|
pixelOffset: new Cesium.Cartesian2(10, 0),
|
||||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
show: false,
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
|
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 1e7),
|
||||||
|
show: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
entity.entryIndex = i;
|
entity.entryIndex = i;
|
||||||
this.markers.push({ entity, index: i });
|
|
||||||
|
// 选中时显示的圆角黑色背景
|
||||||
|
const bg = makeRoundedBg(entry.name, 19);
|
||||||
|
const bgEntity = viewer.entities.add({
|
||||||
|
position: Cesium.Cartesian3.fromDegrees(entry.lng, entry.lat),
|
||||||
|
billboard: {
|
||||||
|
image: bg.canvas,
|
||||||
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
||||||
|
verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
||||||
|
pixelOffset: new Cesium.Cartesian2(10 + bg.textW / 2, 0),
|
||||||
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.markers.push({ entity, bg: bgEntity, index: i });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupPick() {
|
_setupPick() {
|
||||||
const handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
|
const viewer = this.viewer;
|
||||||
|
// 移除 Viewer 默认的单击选中与双击“追踪实体”:否则双击后会锁定相机(定住)
|
||||||
|
viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
||||||
|
viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
|
||||||
|
|
||||||
|
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
||||||
handler.setInputAction((movement) => {
|
handler.setInputAction((movement) => {
|
||||||
const picked = this.viewer.scene.pick(movement.position);
|
const picked = viewer.scene.pick(movement.position);
|
||||||
if (Cesium.defined(picked) && picked.id && picked.id.entryIndex !== undefined) {
|
if (Cesium.defined(picked) && picked.id && picked.id.entryIndex !== undefined) {
|
||||||
const index = picked.id.entryIndex;
|
const index = picked.id.entryIndex;
|
||||||
this.onPick?.(index);
|
this.onPick?.(index);
|
||||||
@@ -160,10 +238,13 @@ export class CesiumGlobe {
|
|||||||
this._handler = handler;
|
this._handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 相机平滑飞至某个标记上空,并将其居中(正俯视,北朝上)
|
// 相机平滑飞至某个标记上空并居中:回到与初始一致的高度与北朝上方向,左键拖拽手感不变
|
||||||
flyToEntry(index) {
|
flyToEntry(index) {
|
||||||
const entry = ENTRIES[index];
|
const entry = ENTRIES[index];
|
||||||
if (!entry || !this.viewer) return;
|
if (!entry || !this.viewer) return;
|
||||||
|
const now = Date.now();
|
||||||
|
if (this._lastFly && now - this._lastFly < 400) return; // 双击/连点去抖,避免重复飞
|
||||||
|
this._lastFly = now;
|
||||||
this.viewer.camera.flyTo({
|
this.viewer.camera.flyTo({
|
||||||
destination: Cesium.Cartesian3.fromDegrees(entry.lng, entry.lat, FLY_TO_HEIGHT),
|
destination: Cesium.Cartesian3.fromDegrees(entry.lng, entry.lat, FLY_TO_HEIGHT),
|
||||||
orientation: {
|
orientation: {
|
||||||
@@ -175,13 +256,121 @@ export class CesiumGlobe {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载行政区划(国家边界,自然地球 110m):把多边形轮廓转成贴合地形的金色细线 + 国家名标签
|
||||||
|
async _loadBoundaries() {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/world-countries.geojson');
|
||||||
|
const geojson = await res.json();
|
||||||
|
const textGold = Cesium.Color.fromCssColorString('#e0c46a');
|
||||||
|
const black = Cesium.Color.fromCssColorString('#14100d');
|
||||||
|
|
||||||
|
const lines = [];
|
||||||
|
for (const feature of geojson.features || []) {
|
||||||
|
const props = feature.properties || {};
|
||||||
|
const geom = feature.geometry;
|
||||||
|
const isPoly = geom && (geom.type === 'Polygon' || geom.type === 'MultiPolygon');
|
||||||
|
const polys = isPoly ? (geom.type === 'MultiPolygon' ? geom.coordinates : [geom.coordinates]) : [];
|
||||||
|
const areaKm2 = isPoly ? sphericalAreaKm2(polys) : 0;
|
||||||
|
|
||||||
|
// 国家名(优先中文),落在 NATURAL EARTH 提供的标注点上,字号按国土面积分级
|
||||||
|
const cname = props.NAME_ZH || props.NAME;
|
||||||
|
if (cname && props.LABEL_X != null && props.LABEL_Y != null) {
|
||||||
|
const fontSize = areaKm2 > 5e6 ? 30 : areaKm2 > 1.5e6 ? 19 : areaKm2 > 3e5 ? 15 : 12;
|
||||||
|
this.viewer.entities.add({
|
||||||
|
position: Cesium.Cartesian3.fromDegrees(props.LABEL_X, props.LABEL_Y),
|
||||||
|
label: {
|
||||||
|
text: cname,
|
||||||
|
font: `bold ${fontSize}px "Noto Serif SC", "Songti SC", "STSong", "SimSun", Georgia, serif`,
|
||||||
|
fillColor: black,
|
||||||
|
style: Cesium.LabelStyle.FILL,
|
||||||
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
|
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(1e6, 1e7),
|
||||||
|
scaleByDistance: new Cesium.NearFarScalar(1e4, 20 / 15, 6e6, 1.0),
|
||||||
|
translucencyByDistance: new Cesium.NearFarScalar(9e6, 1.0, 1e7, 0.0),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isPoly) continue;
|
||||||
|
for (const poly of polys) {
|
||||||
|
for (const ring of poly) {
|
||||||
|
if (ring.length < 2) continue;
|
||||||
|
const flat = [];
|
||||||
|
for (const [lng, lat] of ring) flat.push(lng, lat);
|
||||||
|
const positions = Cesium.Cartesian3.fromDegreesArray(flat);
|
||||||
|
// 行政区划:一根黑线
|
||||||
|
lines.push(new Cesium.GeometryInstance({ geometry: new Cesium.GroundPolylineGeometry({ positions, width: 2.0 }) }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lines.length) {
|
||||||
|
this.viewer.scene.groundPrimitives.add(
|
||||||
|
new Cesium.GroundPolylinePrimitive({
|
||||||
|
geometryInstances: lines,
|
||||||
|
appearance: new Cesium.PolylineMaterialAppearance({
|
||||||
|
material: Cesium.Material.fromType('Color', { color: black }),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('[CesiumGlobe] 行政区划加载失败:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全部城市:Natural Earth 10m 人口稠密点(7342 个),中文名直接用数据自带的 name_zh
|
||||||
|
async _buildCityLabels() {
|
||||||
|
const viewer = this.viewer;
|
||||||
|
const textGold = Cesium.Color.fromCssColorString('#e0c46a');
|
||||||
|
const black = Cesium.Color.fromCssColorString('#14100d');
|
||||||
|
const ddc = new Cesium.DistanceDisplayCondition(0, 4e6);
|
||||||
|
const tdc = new Cesium.NearFarScalar(2e6, 1.0, 4e6, 0.0);
|
||||||
|
|
||||||
|
let cities = [];
|
||||||
|
try {
|
||||||
|
const res = await fetch('/world-cities-zh.json');
|
||||||
|
cities = await res.json();
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('[CesiumGlobe] 城市数据加载失败:', err);
|
||||||
|
cities = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const c of cities) {
|
||||||
|
viewer.entities.add({
|
||||||
|
position: Cesium.Cartesian3.fromDegrees(c.lng, c.lat),
|
||||||
|
point: {
|
||||||
|
pixelSize: 3,
|
||||||
|
color: textGold,
|
||||||
|
outlineColor: black,
|
||||||
|
outlineWidth: 1,
|
||||||
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
|
distanceDisplayCondition: ddc,
|
||||||
|
translucencyByDistance: tdc,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
text: c.name,
|
||||||
|
font: 'bold 14px "Noto Serif SC", "Songti SC", "STSong", "SimSun", Georgia, serif',
|
||||||
|
fillColor: black,
|
||||||
|
style: Cesium.LabelStyle.FILL,
|
||||||
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
||||||
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||||
|
pixelOffset: new Cesium.Cartesian2(0, -6),
|
||||||
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||||
|
distanceDisplayCondition: ddc,
|
||||||
|
translucencyByDistance: tdc,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setSelected(index) {
|
setSelected(index) {
|
||||||
this.selected = index;
|
this.selected = index;
|
||||||
if (!this.viewer) return;
|
if (!this.viewer) return;
|
||||||
this.markers.forEach(({ entity, index: i }) => {
|
this.markers.forEach(({ entity, bg, index: i }) => {
|
||||||
const on = i === index;
|
const on = i === index;
|
||||||
entity.point.pixelSize = on ? 12 : 8;
|
entity.point.outlineWidth = on ? 3 : 1; // 选中加粗黑圈,不放大
|
||||||
entity.label.show = on;
|
if (bg) bg.show = on;
|
||||||
});
|
});
|
||||||
this.viewer.scene.requestRender();
|
this.viewer.scene.requestRender();
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-20
@@ -2,8 +2,11 @@ import * as THREE from 'three';
|
|||||||
import { ENTRIES } from '../data/entries.js';
|
import { ENTRIES } from '../data/entries.js';
|
||||||
import { getPageTextures } from '../textures/pages.js';
|
import { getPageTextures } from '../textures/pages.js';
|
||||||
import { makeTextSprite } from '../utils/labels.js';
|
import { makeTextSprite } from '../utils/labels.js';
|
||||||
|
import { clamp, easeInOutCubic } from '../utils/easing.js';
|
||||||
|
|
||||||
export const TIMELINE_SPREAD = 6.2; // 横轴范围 ±6.2(超出视口,可横向滚动)
|
// 活页之间固定间距(世界单位),横向可滚动
|
||||||
|
const CARD_GAP = 0.65;
|
||||||
|
export const TIMELINE_SPREAD = ((ENTRIES.length - 1) * CARD_GAP) / 2;
|
||||||
|
|
||||||
const CARD_FLOAT = 0.14; // 选中时上浮高度
|
const CARD_FLOAT = 0.14; // 选中时上浮高度
|
||||||
const NAME_Y = 0.42; // 主题标签基准高度(活页上方)
|
const NAME_Y = 0.42; // 主题标签基准高度(活页上方)
|
||||||
@@ -11,7 +14,7 @@ const AXIS_Y = -0.40; // 时间轴基线高度
|
|||||||
const TIME_Y = -0.56; // 时间标签高度(轴下方)
|
const TIME_Y = -0.56; // 时间标签高度(轴下方)
|
||||||
|
|
||||||
function cardX(i, n) {
|
function cardX(i, n) {
|
||||||
return (i / (n - 1)) * 2 * TIMELINE_SPREAD - TIMELINE_SPREAD;
|
return (i - (n - 1) / 2) * CARD_GAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Timeline {
|
export class Timeline {
|
||||||
@@ -27,7 +30,7 @@ export class Timeline {
|
|||||||
_buildLine() {
|
_buildLine() {
|
||||||
const n = ENTRIES.length;
|
const n = ENTRIES.length;
|
||||||
// 轴线向两侧延伸足够远(并关闭雾化),滚动到边缘也不见端点,视觉上无限长
|
// 轴线向两侧延伸足够远(并关闭雾化),滚动到边缘也不见端点,视觉上无限长
|
||||||
const extent = 50;
|
const extent = TIMELINE_SPREAD + 5;
|
||||||
|
|
||||||
const lineMat = new THREE.LineBasicMaterial({ color: 0xc9a227, transparent: true, opacity: 0.7, fog: false });
|
const lineMat = new THREE.LineBasicMaterial({ color: 0xc9a227, transparent: true, opacity: 0.7, fog: false });
|
||||||
const lineGeo = new THREE.BufferGeometry().setFromPoints([
|
const lineGeo = new THREE.BufferGeometry().setFromPoints([
|
||||||
@@ -48,25 +51,30 @@ export class Timeline {
|
|||||||
|
|
||||||
_buildCards() {
|
_buildCards() {
|
||||||
const n = ENTRIES.length;
|
const n = ENTRIES.length;
|
||||||
const cardGeo = new THREE.PlaneGeometry(0.4, 0.565);
|
|
||||||
|
|
||||||
ENTRIES.forEach((entry, i) => {
|
ENTRIES.forEach((entry, i) => {
|
||||||
const x = cardX(i, n);
|
const x = cardX(i, n);
|
||||||
const { front } = getPageTextures(entry);
|
const { front, back } = getPageTextures(entry);
|
||||||
|
|
||||||
const mat = new THREE.MeshStandardMaterial({ map: front, roughness: 0.85, metalness: 0.0 });
|
// 每张卡片独立几何(带分段,便于翻面时弯曲出纸感)
|
||||||
const card = new THREE.Mesh(cardGeo, mat);
|
const cardGeo = new THREE.PlaneGeometry(0.4, 0.565, 24, 6);
|
||||||
card.position.set(x, 0, 0);
|
const base = Float32Array.from(cardGeo.attributes.position.array);
|
||||||
card.userData = { view: 'timeline', index: i };
|
|
||||||
card.castShadow = true;
|
|
||||||
|
|
||||||
const outline = new THREE.Mesh(
|
// 翻转容器:正面登记卡 + 背面素描
|
||||||
new THREE.PlaneGeometry(0.4 * 1.1, 0.565 * 1.1),
|
const flipGroup = new THREE.Group();
|
||||||
new THREE.MeshBasicMaterial({ color: 0xc9a227, transparent: true, opacity: 0.9, side: THREE.BackSide })
|
flipGroup.position.set(x, 0, 0);
|
||||||
);
|
|
||||||
outline.position.z = -0.01;
|
const frontMat = new THREE.MeshStandardMaterial({ map: front, roughness: 0.85, metalness: 0.0, side: THREE.FrontSide });
|
||||||
outline.visible = false;
|
const frontMesh = new THREE.Mesh(cardGeo, frontMat);
|
||||||
card.add(outline);
|
frontMesh.userData = { view: 'timeline', index: i };
|
||||||
|
frontMesh.castShadow = true;
|
||||||
|
|
||||||
|
const backMat = new THREE.MeshStandardMaterial({ map: back, roughness: 0.88, metalness: 0.0, side: THREE.BackSide });
|
||||||
|
const backMesh = new THREE.Mesh(cardGeo, backMat);
|
||||||
|
backMesh.position.z = -0.001;
|
||||||
|
backMesh.userData = { view: 'timeline', index: i };
|
||||||
|
|
||||||
|
flipGroup.add(frontMesh, backMesh);
|
||||||
|
|
||||||
// 活页上方:主题(常显,随选中上浮,超宽居中换行)
|
// 活页上方:主题(常显,随选中上浮,超宽居中换行)
|
||||||
const name = makeTextSprite(entry.name, { fontSize: 17, color: '#e8d9b4', scale: 0.12, maxWidth: 0.9 });
|
const name = makeTextSprite(entry.name, { fontSize: 17, color: '#e8d9b4', scale: 0.12, maxWidth: 0.9 });
|
||||||
@@ -76,8 +84,8 @@ export class Timeline {
|
|||||||
const time = makeTextSprite(entry.time, { fontSize: 13, color: '#c9a227', scale: 0.11 });
|
const time = makeTextSprite(entry.time, { fontSize: 13, color: '#c9a227', scale: 0.11 });
|
||||||
time.position.set(x, TIME_Y, 0);
|
time.position.set(x, TIME_Y, 0);
|
||||||
|
|
||||||
this.group.add(card, name, time);
|
this.group.add(flipGroup, name, time);
|
||||||
this.cards.push({ card, outline, name, nameBaseY: NAME_Y, index: i });
|
this.cards.push({ card: flipGroup, front: frontMesh, back: backMesh, geo: cardGeo, base, name, nameBaseY: NAME_Y, index: i, flipped: false, flipAnim: null });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +93,6 @@ export class Timeline {
|
|||||||
this.selected = index;
|
this.selected = index;
|
||||||
this.cards.forEach((c) => {
|
this.cards.forEach((c) => {
|
||||||
const on = c.index === index;
|
const on = c.index === index;
|
||||||
c.outline.visible = on;
|
|
||||||
c.card.position.y = on ? CARD_FLOAT : 0;
|
c.card.position.y = on ? CARD_FLOAT : 0;
|
||||||
c.name.position.y = c.nameBaseY + (on ? CARD_FLOAT : 0);
|
c.name.position.y = c.nameBaseY + (on ? CARD_FLOAT : 0);
|
||||||
});
|
});
|
||||||
@@ -95,4 +102,36 @@ export class Timeline {
|
|||||||
getCardX(index) {
|
getCardX(index) {
|
||||||
return cardX(index, ENTRIES.length);
|
return cardX(index, ENTRIES.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 点击翻转:每次点击切换正/反面,带旋转动画
|
||||||
|
flip(index) {
|
||||||
|
const c = this.cards[index];
|
||||||
|
if (!c) return;
|
||||||
|
c.flipped = !c.flipped;
|
||||||
|
c.flipAnim = { from: c.card.rotation.y, to: c.flipped ? Math.PI : 0, t: 0, dur: 0.6 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 翻面动画(由 App 每帧调用)
|
||||||
|
update(dt) {
|
||||||
|
this.cards.forEach((c) => {
|
||||||
|
if (!c.flipAnim) return;
|
||||||
|
const a = c.flipAnim;
|
||||||
|
a.t += dt / a.dur;
|
||||||
|
const k = clamp(a.t, 0, 1);
|
||||||
|
c.card.rotation.y = a.from + (a.to - a.from) * easeInOutCubic(k);
|
||||||
|
|
||||||
|
// 纸张弯曲:翻面中间段边缘向后微弯,带柔软感
|
||||||
|
const bend = Math.sin(k * Math.PI) * 0.06;
|
||||||
|
const pos = c.geo.attributes.position;
|
||||||
|
const arr = pos.array;
|
||||||
|
const base = c.base;
|
||||||
|
for (let j = 0; j < arr.length; j += 3) {
|
||||||
|
const xn = base[j] / 0.4; // -0.5 .. 0.5
|
||||||
|
arr[j + 2] = base[j + 2] - bend * (xn * 2) * (xn * 2);
|
||||||
|
}
|
||||||
|
pos.needsUpdate = true;
|
||||||
|
|
||||||
|
if (k >= 1) c.flipAnim = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-8
@@ -61,12 +61,12 @@ html, body {
|
|||||||
}
|
}
|
||||||
.subtitle {
|
.subtitle {
|
||||||
margin-top: 4px; font-size: 12px; letter-spacing: 0.18em;
|
margin-top: 4px; font-size: 12px; letter-spacing: 0.18em;
|
||||||
color: rgba(230,216,182,0.65); font-family: Georgia, serif;
|
color: rgba(230,216,182,0.65); font-family: var(--serif);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 视图切换 */
|
/* 视图切换(位于底部导航栏内) */
|
||||||
.view-switch {
|
.view-switch {
|
||||||
top: 26px; right: 26px; display: flex; gap: 8px;
|
display: flex; align-items: center; gap: 8px;
|
||||||
}
|
}
|
||||||
.view-btn {
|
.view-btn {
|
||||||
font-family: var(--serif); font-size: 14px; letter-spacing: 0.1em;
|
font-family: var(--serif); font-size: 14px; letter-spacing: 0.1em;
|
||||||
@@ -100,10 +100,15 @@ html, body {
|
|||||||
.nav-btn:active { transform: scale(0.96); }
|
.nav-btn:active { transform: scale(0.96); }
|
||||||
.flip-hint { width: auto; border-radius: 20px; padding: 0 18px; letter-spacing: 0.12em; font-size: 14px; }
|
.flip-hint { width: auto; border-radius: 20px; padding: 0 18px; letter-spacing: 0.12em; font-size: 14px; }
|
||||||
.page-indicator { display: flex; align-items: baseline; gap: 6px; min-width: 200px; justify-content: center; }
|
.page-indicator { display: flex; align-items: baseline; gap: 6px; min-width: 200px; justify-content: center; }
|
||||||
#page-num { font-size: 24px; color: var(--gold-soft); font-family: Georgia, serif; }
|
#page-num { font-size: 24px; color: var(--gold-soft); font-family: var(--serif); }
|
||||||
.sep { color: rgba(230,216,182,0.5); }
|
.sep { color: rgba(230,216,182,0.5); }
|
||||||
#page-total { color: rgba(230,216,182,0.6); font-family: Georgia, serif; }
|
#page-total { color: rgba(230,216,182,0.6); font-family: var(--serif); }
|
||||||
.page-name { font-size: 14px; color: var(--paper); letter-spacing: 0.05em; margin-left: 10px; }
|
.page-name {
|
||||||
|
font-size: 14px; color: var(--paper); letter-spacing: 0.05em; margin-left: 10px;
|
||||||
|
flex: 0 0 160px; /* 固定宽度,容纳多数词条名,避免翻页时布局抖动 */
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
/* 登记卡详情 */
|
/* 登记卡详情 */
|
||||||
.detail-card {
|
.detail-card {
|
||||||
@@ -115,7 +120,7 @@ html, body {
|
|||||||
}
|
}
|
||||||
.detail-card.hidden { opacity: 0; pointer-events: none; transform: translateY(14px); }
|
.detail-card.hidden { opacity: 0; pointer-events: none; transform: translateY(14px); }
|
||||||
.detail-head { display: flex; justify-content: space-between; align-items: center; }
|
.detail-head { display: flex; justify-content: space-between; align-items: center; }
|
||||||
.detail-no { font-family: Georgia, serif; font-size: 13px; letter-spacing: 0.14em; color: var(--ink-soft); }
|
.detail-no { font-family: var(--serif); font-size: 13px; letter-spacing: 0.14em; color: var(--ink-soft); }
|
||||||
.detail-cat {
|
.detail-cat {
|
||||||
font-size: 12px; padding: 2px 10px; border-radius: 2px;
|
font-size: 12px; padding: 2px 10px; border-radius: 2px;
|
||||||
background: var(--leather); color: var(--gold-soft); letter-spacing: 0.2em;
|
background: var(--leather); color: var(--gold-soft); letter-spacing: 0.2em;
|
||||||
@@ -134,7 +139,7 @@ html, body {
|
|||||||
border-left: 3px solid var(--gold); padding-left: 10px;
|
border-left: 3px solid var(--gold); padding-left: 10px;
|
||||||
}
|
}
|
||||||
.detail-sketch { margin-top: 12px; border-top: 1px dashed rgba(90,61,43,0.4); padding-top: 10px; }
|
.detail-sketch { margin-top: 12px; border-top: 1px dashed rgba(90,61,43,0.4); padding-top: 10px; }
|
||||||
.detail-sketch canvas { width: 100%; height: auto; border-radius: 2px; opacity: 0.94; }
|
.detail-sketch canvas { width: 100%; height: 170px; object-fit: contain; border-radius: 2px; opacity: 0.94; }
|
||||||
|
|
||||||
/* 提示 */
|
/* 提示 */
|
||||||
.hint {
|
.hint {
|
||||||
|
|||||||
+49
-49
@@ -7,13 +7,17 @@ export const PAGE_W = 1.6; // 世界单位:页宽
|
|||||||
export const PAGE_H = 2.24; // 世界单位:页高
|
export const PAGE_H = 2.24; // 世界单位:页高
|
||||||
export const TEX_W = 768;
|
export const TEX_W = 768;
|
||||||
export const TEX_H = 1075;
|
export const TEX_H = 1075;
|
||||||
|
// 输出纹理与绘制分辨率一致,保证文字清晰
|
||||||
|
export const TEX_OUT_W = 768;
|
||||||
|
export const TEX_OUT_H = 1075;
|
||||||
export const HOLE_U = [0.1, 0.26, 0.42, 0.58, 0.74, 0.9]; // 活页孔(自顶部归一化,6孔等距)
|
export const HOLE_U = [0.1, 0.26, 0.42, 0.58, 0.74, 0.9]; // 活页孔(自顶部归一化,6孔等距)
|
||||||
export const HOLE_X = 0.065; // 孔中心相对页内缘(环前交点 0.085 − 页缘空隙 0.02)
|
export const HOLE_X = 0.065; // 孔中心相对页内缘(环前交点 0.085 − 页缘空隙 0.02)
|
||||||
export const HOLE_R = 0.029; // 孔半径 ≈5.5mm 直径(真实活页本孔径)
|
export const HOLE_R = 0.029; // 孔半径 ≈5.5mm 直径(真实活页本孔径)
|
||||||
|
|
||||||
const SERIF = '"Noto Serif SC","Songti SC","STSong","SimSun",Georgia,serif';
|
const SERIF = 'Georgia, "Noto Serif SC", serif'; // 正文与标题字体一致(时间轴字体)
|
||||||
const KAI = '"Kaiti SC","STKaiti","KaiTi","Noto Serif SC",serif';
|
const KAI = '"Kaiti SC","STKaiti","KaiTi","Noto Serif SC",serif';
|
||||||
const NUM = 'Georgia,"Times New Roman",serif';
|
const NUM = 'Georgia,"Times New Roman",serif';
|
||||||
|
const TITLE_FONT = '"Noto Serif SC", "Songti SC", "STSong", "SimSun", Georgia, serif'; // 与时间轴标题字体一致
|
||||||
|
|
||||||
// 版面边距(左边缘避开活页孔,右/上/下为常规边距)
|
// 版面边距(左边缘避开活页孔,右/上/下为常规边距)
|
||||||
const MARGIN = { left: 86, right: 58, top: 50, bottom: 50 };
|
const MARGIN = { left: 86, right: 58, top: 50, bottom: 50 };
|
||||||
@@ -65,6 +69,28 @@ export function makePaper(w, h, { tone = 0, ruled = false, margin = false, seed
|
|||||||
ctx.fillRect(0, 0, w, h);
|
ctx.fillRect(0, 0, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 烫金斑点:不规则金色斑点(淡、纯色、硬边)
|
||||||
|
ctx.fillStyle = 'rgba(212, 168, 64, 0.5)';
|
||||||
|
const goldBlob = (x, y, r) => {
|
||||||
|
const pts = 8;
|
||||||
|
ctx.beginPath();
|
||||||
|
for (let p = 0; p < pts; p++) {
|
||||||
|
const a = (p / pts) * Math.PI * 2;
|
||||||
|
const rr = r * (0.55 + rand() * 0.9);
|
||||||
|
const px = x + Math.cos(a) * rr;
|
||||||
|
const py = y + Math.sin(a) * rr;
|
||||||
|
if (p === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py);
|
||||||
|
}
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
};
|
||||||
|
for (let i = 0; i < 70; i++) {
|
||||||
|
goldBlob(rand() * w, rand() * h, rand() * 8 + 3);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 320; i++) {
|
||||||
|
goldBlob(rand() * w, rand() * h, rand() * 2.4 + 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
// 边缘做旧(暗角)
|
// 边缘做旧(暗角)
|
||||||
const edge = ctx.createRadialGradient(w / 2, h / 2, Math.min(w, h) * 0.4, w / 2, h / 2, Math.max(w, h) * 0.72);
|
const edge = ctx.createRadialGradient(w / 2, h / 2, Math.min(w, h) * 0.4, w / 2, h / 2, Math.max(w, h) * 0.72);
|
||||||
edge.addColorStop(0, 'rgba(0,0,0,0)');
|
edge.addColorStop(0, 'rgba(0,0,0,0)');
|
||||||
@@ -174,20 +200,18 @@ export function renderFront(entry) {
|
|||||||
drawBorder(ctx, w, h);
|
drawBorder(ctx, w, h);
|
||||||
drawHoles(ctx, w, h);
|
drawHoles(ctx, w, h);
|
||||||
|
|
||||||
// 页眉:编号 + 印章
|
// 页眉:编号(去掉右上角印章标记)
|
||||||
text(ctx, '编 号 · ' + entry.id, CONTENT_L, 92, { font: NUM, size: 26, color: '#5a432c' });
|
text(ctx, '编 号 · ' + entry.id, CONTENT_L, 92, { font: NUM, size: 26, color: '#5a432c' });
|
||||||
drawSeal(ctx, entry.category, CONTENT_R - 30, 86, 58);
|
|
||||||
text(ctx, info.en, CONTENT_R - 30, 128, { font: NUM, size: 15, color: '#7a6147', align: 'center' });
|
|
||||||
|
|
||||||
// 顶部分隔线
|
// 顶部分隔线
|
||||||
ctx.strokeStyle = 'rgba(90,61,43,0.4)';
|
ctx.strokeStyle = 'rgba(90,61,43,0.4)';
|
||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = 1;
|
||||||
ctx.beginPath(); ctx.moveTo(CONTENT_L, 160); ctx.lineTo(CONTENT_R, 160); ctx.stroke();
|
ctx.beginPath(); ctx.moveTo(CONTENT_L, 160); ctx.lineTo(CONTENT_R, 160); ctx.stroke();
|
||||||
|
|
||||||
// 名称(大字,居中)
|
// 名称(大字,居中,标题字体与时间轴一致)
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
ctx.font = `700 ${fitFont(ctx, entry.name, CONTENT_W - 40, 36, 86)}px ${KAI}`;
|
ctx.font = `700 ${fitFont(ctx, entry.name, CONTENT_W - 40, 36, 86, TITLE_FONT)}px ${TITLE_FONT}`;
|
||||||
ctx.fillStyle = '#33271a';
|
ctx.fillStyle = '#33271a';
|
||||||
ctx.fillText(entry.name, w / 2, 248);
|
ctx.fillText(entry.name, w / 2, 248);
|
||||||
// 名称下划线装饰
|
// 名称下划线装饰
|
||||||
@@ -205,7 +229,6 @@ export function renderFront(entry) {
|
|||||||
const fields = [
|
const fields = [
|
||||||
['时间', entry.time],
|
['时间', entry.time],
|
||||||
['地区', entry.region],
|
['地区', entry.region],
|
||||||
['类别', `${entry.category} · ${info.en}`],
|
|
||||||
];
|
];
|
||||||
fields.forEach(([label, value], i) => {
|
fields.forEach(([label, value], i) => {
|
||||||
const y = fy + i * rowH;
|
const y = fy + i * rowH;
|
||||||
@@ -243,10 +266,6 @@ export function renderFront(entry) {
|
|||||||
}
|
}
|
||||||
if (line) ctx.fillText(line, CONTENT_L, yy);
|
if (line) ctx.fillText(line, CONTENT_L, yy);
|
||||||
|
|
||||||
// 页脚
|
|
||||||
text(ctx, '人类文明里程碑 · 活页编年史', w / 2, h - 52, { font: SERIF, size: 19, color: '#7a6147', align: 'center' });
|
|
||||||
text(ctx, entry.id, CONTENT_R, h - 52, { font: NUM, size: 18, color: '#8a735a', align: 'right' });
|
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,55 +283,24 @@ export function renderBack(entry) {
|
|||||||
ctx.scale(-1, 1);
|
ctx.scale(-1, 1);
|
||||||
ctx.translate(-w / 2, 0);
|
ctx.translate(-w / 2, 0);
|
||||||
|
|
||||||
// 页眉(居中)
|
// 速写(居中,无文字与装饰线)
|
||||||
text(ctx, '素 描 · SKETCH', w / 2, 92, { font: NUM, size: 24, color: '#6b5740', align: 'center' });
|
|
||||||
text(ctx, entry.id, w / 2, 124, { font: NUM, size: 20, color: '#8a735a', align: 'center' });
|
|
||||||
ctx.strokeStyle = 'rgba(90,61,43,0.35)';
|
|
||||||
ctx.lineWidth = 1;
|
|
||||||
ctx.beginPath(); ctx.moveTo(w / 2 - 90, 152); ctx.lineTo(w / 2 + 90, 152); ctx.stroke();
|
|
||||||
|
|
||||||
// 速写(居中)
|
|
||||||
const sketch = renderSketch(entry, 520);
|
const sketch = renderSketch(entry, 520);
|
||||||
const sx = (w - 520) / 2;
|
const sx = (w - 520) / 2;
|
||||||
const sy = 170;
|
const sy = (h - 520) / 2;
|
||||||
ctx.drawImage(sketch, sx, sy);
|
ctx.drawImage(sketch, sx, sy);
|
||||||
ctx.strokeStyle = 'rgba(90,61,43,0.3)';
|
|
||||||
ctx.lineWidth = 0.8;
|
|
||||||
ctx.strokeRect(sx - 8, sy - 8, 520 + 16, 520 + 16);
|
|
||||||
|
|
||||||
// 题注(居中)
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.textBaseline = 'middle';
|
|
||||||
ctx.font = `700 ${fitFont(ctx, entry.name, CONTENT_W - 40, 32, 78)}px ${KAI}`;
|
|
||||||
ctx.fillStyle = '#33271a';
|
|
||||||
ctx.fillText(entry.name, w / 2, 772);
|
|
||||||
|
|
||||||
ctx.font = `400 25px ${SERIF}`;
|
|
||||||
ctx.fillStyle = '#6b5740';
|
|
||||||
ctx.fillText(`${entry.time} · ${entry.region}`, w / 2, 828);
|
|
||||||
|
|
||||||
// 波浪线(居中)
|
|
||||||
ctx.strokeStyle = 'rgba(90,61,43,0.5)';
|
|
||||||
ctx.lineWidth = 1.4;
|
|
||||||
ctx.beginPath();
|
|
||||||
for (let x = w / 2 - 130; x <= w / 2 + 130; x += 10) {
|
|
||||||
const yy = 868 + Math.sin((x - w / 2) / 26) * 5;
|
|
||||||
if (x === w / 2 - 130) ctx.moveTo(x, yy); else ctx.lineTo(x, yy);
|
|
||||||
}
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 名称自适应字号
|
// 名称自适应字号
|
||||||
function fitFont(ctx, str, maxW, minSize, maxSize) {
|
function fitFont(ctx, str, maxW, minSize, maxSize, font = KAI) {
|
||||||
if (minSize > maxSize) [minSize, maxSize] = [maxSize, minSize]; // 容错:确保 min < max
|
if (minSize > maxSize) [minSize, maxSize] = [maxSize, minSize]; // 容错:确保 min < max
|
||||||
let size = maxSize;
|
let size = maxSize;
|
||||||
ctx.font = `700 ${size}px ${KAI}`;
|
ctx.font = `700 ${size}px ${font}`;
|
||||||
while (ctx.measureText(str).width > maxW && size > minSize) {
|
while (ctx.measureText(str).width > maxW && size > minSize) {
|
||||||
size -= 2;
|
size -= 2;
|
||||||
ctx.font = `700 ${size}px ${KAI}`;
|
ctx.font = `700 ${size}px ${font}`;
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@@ -323,15 +311,27 @@ function hash(str) {
|
|||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将高分辨率画布缩小到目标尺寸(保持版面不变,降低显存占用)
|
||||||
|
function downscale(src) {
|
||||||
|
const c = document.createElement('canvas');
|
||||||
|
c.width = TEX_OUT_W;
|
||||||
|
c.height = TEX_OUT_H;
|
||||||
|
const ctx = c.getContext('2d');
|
||||||
|
ctx.imageSmoothingEnabled = true;
|
||||||
|
ctx.imageSmoothingQuality = 'high';
|
||||||
|
ctx.drawImage(src, 0, 0, TEX_OUT_W, TEX_OUT_H);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
// 生成页面的两个纹理
|
// 生成页面的两个纹理
|
||||||
const texCache = new Map();
|
const texCache = new Map();
|
||||||
export function getPageTextures(entry) {
|
export function getPageTextures(entry) {
|
||||||
if (texCache.has(entry.id)) return texCache.get(entry.id);
|
if (texCache.has(entry.id)) return texCache.get(entry.id);
|
||||||
const front = new THREE.CanvasTexture(renderFront(entry));
|
const front = new THREE.CanvasTexture(downscale(renderFront(entry)));
|
||||||
front.colorSpace = THREE.SRGBColorSpace;
|
front.colorSpace = THREE.SRGBColorSpace;
|
||||||
front.anisotropy = 8;
|
front.anisotropy = 8;
|
||||||
|
|
||||||
const back = new THREE.CanvasTexture(renderBack(entry));
|
const back = new THREE.CanvasTexture(downscale(renderBack(entry)));
|
||||||
back.colorSpace = THREE.SRGBColorSpace;
|
back.colorSpace = THREE.SRGBColorSpace;
|
||||||
back.anisotropy = 8;
|
back.anisotropy = 8;
|
||||||
|
|
||||||
|
|||||||
+66
-1
@@ -31,7 +31,7 @@ export function renderSketch(entry, size = 760) {
|
|||||||
ctx.clearRect(0, 0, size, size);
|
ctx.clearRect(0, 0, size, size);
|
||||||
const cx = size / 2;
|
const cx = size / 2;
|
||||||
const cy = size / 2;
|
const cy = size / 2;
|
||||||
const r = size * 0.42;
|
const r = size * 0.48;
|
||||||
|
|
||||||
const P = (x, y) => [cx + x * r, cy + y * r];
|
const P = (x, y) => [cx + x * r, cy + y * r];
|
||||||
|
|
||||||
@@ -579,6 +579,71 @@ export function renderSketch(entry, size = 760) {
|
|||||||
line([[0.4, 0.5], [0.4, 0.3]], { lw: 1.4 });
|
line([[0.4, 0.5], [0.4, 0.3]], { lw: 1.4 });
|
||||||
line([[0.4, 0.3], [0.56, 0.34], [0.4, 0.4]], { lw: 1.4, passes: 1 });
|
line([[0.4, 0.3], [0.56, 0.34], [0.4, 0.4]], { lw: 1.4, passes: 1 });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
book() {
|
||||||
|
// 摊开的书
|
||||||
|
fill([[-0.5, -0.1], [0.0, -0.2], [0.5, -0.1], [0.5, 0.4], [0.0, 0.5], [-0.5, 0.4]], 'rgba(210,190,150,0.18)');
|
||||||
|
line([[-0.5, -0.1], [0.0, -0.2], [0.5, -0.1], [0.5, 0.4], [0.0, 0.5], [-0.5, 0.4], [-0.5, -0.1]], { lw: 2.4 });
|
||||||
|
line([[0.0, -0.2], [0.0, 0.5]], { lw: 2 });
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const y = -0.05 + i * 0.14;
|
||||||
|
line([[-0.4, y], [-0.08, y]], { lw: 1.2, color: INK_SOFT, passes: 1, wobble: 0.004 });
|
||||||
|
line([[0.08, y], [0.4, y]], { lw: 1.2, color: INK_SOFT, passes: 1, wobble: 0.004 });
|
||||||
|
}
|
||||||
|
hatch([[-0.5, -0.1], [0.0, -0.2], [0.5, -0.1], [0.5, 0.4], [0.0, 0.5], [-0.5, 0.4]], { spacing: 0.04, angle: -45, color: INK_FAINT, lw: 1 });
|
||||||
|
},
|
||||||
|
|
||||||
|
bridge() {
|
||||||
|
line([[-0.7, 0.45], [0.7, 0.45]], { lw: 1.6, color: INK_SOFT, passes: 1 });
|
||||||
|
line([[-0.6, 0.6], [0.6, 0.6]], { lw: 1.2, color: INK_FAINT, passes: 1 });
|
||||||
|
fill([[-0.5, 0.45], [-0.5, -0.05], [0.5, -0.05], [0.5, 0.45]], 'rgba(150,140,130,0.10)');
|
||||||
|
line([[-0.5, 0.45], [-0.5, 0.05], [-0.35, -0.2], [0.0, -0.35], [0.35, -0.2], [0.5, 0.05], [0.5, 0.45]], { lw: 2.6 });
|
||||||
|
line([[-0.55, 0.45], [-0.55, 0.28], [0.55, 0.28], [0.55, 0.45]], { lw: 1.6, color: INK_SOFT });
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
const x = -0.45 + i * 0.15;
|
||||||
|
line([[x, 0.28], [x, 0.4]], { lw: 1.2, passes: 1, color: INK_SOFT });
|
||||||
|
}
|
||||||
|
line([[-0.3, 0.52], [0.0, 0.56], [0.3, 0.52]], { lw: 1, color: INK_FAINT, passes: 1 });
|
||||||
|
},
|
||||||
|
|
||||||
|
flag() {
|
||||||
|
line([[-0.4, 0.6], [-0.4, -0.7]], { lw: 2.4 });
|
||||||
|
fill([[-0.4, -0.65], [-0.1, -0.6], [0.05, -0.45], [-0.25, -0.5]], 'rgba(160,58,42,0.16)');
|
||||||
|
line([[-0.4, -0.65], [-0.1, -0.6], [0.05, -0.45], [-0.25, -0.5], [-0.4, -0.65]], { lw: 2.2 });
|
||||||
|
line([[-0.32, -0.55], [0.0, -0.48]], { lw: 1.4, color: INK_SOFT, passes: 1 });
|
||||||
|
line([[-0.25, -0.5], [-0.15, -0.32]], { lw: 1.4, color: INK_SOFT, passes: 1 });
|
||||||
|
line([[-0.6, 0.62], [0.6, 0.62]], { lw: 1.6, color: INK_FAINT });
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
const a = -0.5 + i * 0.3;
|
||||||
|
line([[0.05 + Math.cos(a) * 0.3, -0.3 + Math.sin(a) * 0.2], [0.05 + Math.cos(a) * 0.42, -0.3 + Math.sin(a) * 0.3]], { lw: 1.2, color: INK_FAINT, passes: 1 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
scale() {
|
||||||
|
line([[0.0, 0.6], [0.0, -0.15]], { lw: 2.4 });
|
||||||
|
line([[-0.55, -0.05], [0.55, -0.05]], { lw: 2.2 });
|
||||||
|
circle(0.0, -0.1, 0.06, { lw: 1.8, passes: 1 });
|
||||||
|
line([[-0.55, -0.05], [-0.55, 0.2]], { lw: 1.6, passes: 1 });
|
||||||
|
line([[0.55, -0.05], [0.55, 0.2]], { lw: 1.6, passes: 1 });
|
||||||
|
line([[-0.72, 0.2], [-0.38, 0.2]], { lw: 2 });
|
||||||
|
line([[0.38, 0.2], [0.72, 0.2]], { lw: 2 });
|
||||||
|
line([[-0.7, 0.2], [-0.72, 0.34], [-0.38, 0.34], [-0.4, 0.2]], { lw: 1.6, color: INK_SOFT, passes: 1 });
|
||||||
|
line([[0.38, 0.2], [0.4, 0.34], [0.7, 0.34], [0.72, 0.2]], { lw: 1.6, color: INK_SOFT, passes: 1 });
|
||||||
|
line([[-0.3, 0.6], [0.3, 0.6]], { lw: 2.6 });
|
||||||
|
line([[-0.2, 0.68], [0.2, 0.68]], { lw: 2 });
|
||||||
|
},
|
||||||
|
|
||||||
|
sword() {
|
||||||
|
line([[0.1, -0.6], [0.0, -0.05], [-0.02, 0.15]], { lw: 2.6 });
|
||||||
|
line([[0.1, -0.6], [0.05, -0.05], [0.02, 0.15]], { lw: 1.4, color: INK_SOFT, passes: 1 });
|
||||||
|
fill([[0.1, -0.6], [0.0, -0.05], [0.05, -0.05]], 'rgba(150,150,160,0.10)');
|
||||||
|
line([[-0.25, 0.05], [0.22, -0.02]], { lw: 3 });
|
||||||
|
line([[-0.02, 0.15], [0.05, 0.42]], { lw: 2.4 });
|
||||||
|
circle(0.02, 0.45, 0.05, { lw: 1.6, passes: 1 });
|
||||||
|
line([[-0.5, 0.6], [0.5, 0.6]], { lw: 1.4, color: INK_FAINT, passes: 1 });
|
||||||
|
line([[0.15, -0.5], [0.28, -0.42]], { lw: 1.2, color: INK_FAINT, passes: 1 });
|
||||||
|
line([[0.18, -0.3], [0.32, -0.22]], { lw: 1, color: INK_FAINT, passes: 1 });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const draw = types[entry.sketch] || types.fossil;
|
const draw = types[entry.sketch] || types.fossil;
|
||||||
|
|||||||
+7
-10
@@ -1,4 +1,3 @@
|
|||||||
import { CATEGORY_INFO } from '../data/entries.js';
|
|
||||||
import { renderSketch } from '../textures/sketch.js';
|
import { renderSketch } from '../textures/sketch.js';
|
||||||
|
|
||||||
// HTML 覆盖层:标题、视图切换、底部导航、登记卡详情
|
// HTML 覆盖层:标题、视图切换、底部导航、登记卡详情
|
||||||
@@ -15,9 +14,7 @@ export class Overlay {
|
|||||||
pageTotal: document.getElementById('page-total'),
|
pageTotal: document.getElementById('page-total'),
|
||||||
pageName: document.getElementById('page-name'),
|
pageName: document.getElementById('page-name'),
|
||||||
detailNo: document.getElementById('detail-no'),
|
detailNo: document.getElementById('detail-no'),
|
||||||
detailCat: document.getElementById('detail-cat'),
|
|
||||||
detailName: document.getElementById('detail-name'),
|
detailName: document.getElementById('detail-name'),
|
||||||
detailName2: document.getElementById('detail-name2'),
|
|
||||||
detailTime: document.getElementById('detail-time'),
|
detailTime: document.getElementById('detail-time'),
|
||||||
detailRegion: document.getElementById('detail-region'),
|
detailRegion: document.getElementById('detail-region'),
|
||||||
detailDesc: document.getElementById('detail-desc'),
|
detailDesc: document.getElementById('detail-desc'),
|
||||||
@@ -35,7 +32,7 @@ export class Overlay {
|
|||||||
});
|
});
|
||||||
document.getElementById('btn-prev').addEventListener('click', () => this.onPrev());
|
document.getElementById('btn-prev').addEventListener('click', () => this.onPrev());
|
||||||
document.getElementById('btn-next').addEventListener('click', () => this.onNext());
|
document.getElementById('btn-next').addEventListener('click', () => this.onNext());
|
||||||
document.getElementById('btn-flip').addEventListener('click', () => this.onFlip());
|
document.getElementById('btn-flip')?.addEventListener('click', () => this.onFlip());
|
||||||
}
|
}
|
||||||
|
|
||||||
setTotal(n) {
|
setTotal(n) {
|
||||||
@@ -55,24 +52,24 @@ export class Overlay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setEntry(entry, index, total) {
|
setEntry(entry, index, total) {
|
||||||
const info = CATEGORY_INFO[entry.category] || CATEGORY_INFO['事'];
|
|
||||||
this.els.pageNum.textContent = String(index + 1).padStart(2, '0');
|
this.els.pageNum.textContent = String(index + 1).padStart(2, '0');
|
||||||
this.els.pageName.textContent = entry.name;
|
this.els.pageName.textContent = entry.name;
|
||||||
this.els.detailNo.textContent = entry.id;
|
this.els.detailNo.textContent = entry.id;
|
||||||
this.els.detailCat.textContent = entry.category;
|
|
||||||
this.els.detailCat.style.background = info.color;
|
|
||||||
this.els.detailName.textContent = entry.name;
|
this.els.detailName.textContent = entry.name;
|
||||||
this.els.detailName2.textContent = entry.name;
|
|
||||||
this.els.detailTime.textContent = entry.time;
|
this.els.detailTime.textContent = entry.time;
|
||||||
this.els.detailRegion.textContent = entry.region;
|
this.els.detailRegion.textContent = entry.region;
|
||||||
this.els.detailDesc.textContent = entry.description;
|
this.els.detailDesc.textContent = entry.description;
|
||||||
|
|
||||||
// 素描缩略图
|
// 素描缩略图(等比放大填满,裁掉上下多余部分)
|
||||||
const c = this.els.detailSketch;
|
const c = this.els.detailSketch;
|
||||||
const ctx = c.getContext('2d');
|
const ctx = c.getContext('2d');
|
||||||
ctx.clearRect(0, 0, c.width, c.height);
|
ctx.clearRect(0, 0, c.width, c.height);
|
||||||
const sketch = renderSketch(entry, 560);
|
const sketch = renderSketch(entry, 560);
|
||||||
ctx.drawImage(sketch, (c.width - c.height) / 2, 0, c.height, c.height);
|
const sw = sketch.width, sh = sketch.height;
|
||||||
|
const cw = c.width, ch = c.height;
|
||||||
|
const scale = Math.min(cw / sw, ch / sh);
|
||||||
|
const dw = sw * scale, dh = sh * scale;
|
||||||
|
ctx.drawImage(sketch, (cw - dw) / 2, (ch - dh) / 2, dw, dh);
|
||||||
}
|
}
|
||||||
|
|
||||||
irisTo(visible) {
|
irisTo(visible) {
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ export function makeTextSprite(text, {
|
|||||||
stroke = 'rgba(20,12,6,0.9)',
|
stroke = 'rgba(20,12,6,0.9)',
|
||||||
strokeWidth = 6,
|
strokeWidth = 6,
|
||||||
padding = 24,
|
padding = 24,
|
||||||
font = 'Georgia, "Noto Serif SC", serif',
|
font = '"Noto Serif SC", "Songti SC", "STSong", "SimSun", Georgia, serif',
|
||||||
weight = '600',
|
weight = '600',
|
||||||
scale = 1, // 单行文字的精灵世界高度
|
scale = 1, // 单行文字的精灵世界高度
|
||||||
maxWidth = Infinity, // 精灵世界最大宽度,超宽则居中换行
|
maxWidth = Infinity, // 精灵世界最大宽度,超宽则居中换行
|
||||||
|
|||||||
Reference in New Issue
Block a user