Spaces:
Running
Running
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>World Citybook | 全球「城市路书」地图</title> | |
| <link rel="icon" href="data:," /> | |
| <link rel="stylesheet" href="src/styles.css?v=20260714-predeploy-010" /> | |
| <link rel="stylesheet" href="src/ticket-clip.css?v=20260714-predeploy-010" /> | |
| </head> | |
| <body> | |
| <main class="atlas-shell"> | |
| <section class="map-stage" aria-label="世界地图底座"> | |
| <canvas id="halftoneCanvas" class="halftone-canvas" aria-hidden="true"></canvas> | |
| <div id="cityTileLayer" class="city-tile-layer" aria-hidden="true"></div> | |
| <header class="hero-copy" aria-label="项目介绍"> | |
| <p class="hero-kicker">全球「城市路书」地图</p> | |
| <h1><span class="hero-title__word hero-title__word--world"><span class="hero-title__initial">W</span><span class="hero-title__rest">orld</span></span><br /><span class="hero-title__word hero-title__word--citybook">Cit<span class="hero-title__citybook-y">y</span>boo<span class="hero-title__citybook-k">k</span></span></h1> | |
| <p class="hero-tagline">自由探索,在空间叙事与地方书写中读懂城市</p> | |
| <form class="search-capsule" role="search" aria-label="搜索城市路书"> | |
| <span class="search-mark" aria-hidden="true"></span> | |
| <input type="search" placeholder="搜索国家、城市、书名、作者或主题" autocomplete="off" aria-controls="searchResults" aria-expanded="false" /> | |
| <button class="search-clear" type="button" aria-label="清除搜索"></button> | |
| <button class="search-suggestion" type="button" hidden aria-label="使用推荐搜索词"></button> | |
| <div id="searchResults" class="search-results" role="listbox" aria-label="搜索推荐" hidden></div> | |
| </form> | |
| </header> | |
| <button id="themeToggle" class="theme-toggle" type="button" aria-label="切换暗色模式" aria-pressed="false"> | |
| <span class="theme-toggle__icon" aria-hidden="true"> | |
| <svg viewBox="0 0 24 24" focusable="false"> | |
| <circle cx="12" cy="12" r="4.2"></circle> | |
| <path d="M12 2.8v2.1M12 19.1v2.1M4.2 4.2l1.5 1.5M18.3 18.3l1.5 1.5M2.8 12h2.1M19.1 12h2.1M4.2 19.8l1.5-1.5M18.3 5.7l1.5-1.5"></path> | |
| </svg> | |
| </span> | |
| <span class="theme-toggle__icon" aria-hidden="true"> | |
| <svg viewBox="0 0 24 24" focusable="false"> | |
| <path d="M18.7 15.5A7.8 7.8 0 0 1 8.5 5.3 7.9 7.9 0 1 0 18.7 15.5Z"></path> | |
| </svg> | |
| </span> | |
| <span class="theme-toggle__knob" aria-hidden="true"></span> | |
| </button> | |
| <button id="bookshelfToggle" class="bookshelf-toggle" type="button" aria-label="打开我的书架"> | |
| <svg class="bookshelf-toggle__icon" viewBox="0 0 24 24" focusable="false" aria-hidden="true"> | |
| <path d="M4.5 19.2h15"></path> | |
| <path d="M6.2 5.4v13.8"></path> | |
| <path d="M10.2 6.3v12.9"></path> | |
| <path d="M14.4 4.8l2.9 13.8"></path> | |
| <path d="M4.5 8.5h6.8"></path> | |
| </svg> | |
| <span id="bookshelfCount" class="bookshelf-toggle__count" aria-hidden="true">0</span> | |
| </button> | |
| <button id="specialBackButton" class="special-back-button" type="button" hidden> | |
| <span class="special-back-button__icon" aria-hidden="true"> | |
| <svg viewBox="0 0 24 24" focusable="false"> | |
| <circle cx="12" cy="12" r="9.25"></circle> | |
| <path d="M2.75 12h18.5M12 2.75a13.8 13.8 0 0 1 0 18.5M12 2.75a13.8 13.8 0 0 0 0 18.5"></path> | |
| </svg> | |
| </span> | |
| <span class="special-back-button__label">返回全球视角</span> | |
| </button> | |
| <p class="drag-hint" aria-hidden="true">── 长按转动地球</p> | |
| <div id="musicSignature" class="music-signature" aria-label="背景音乐"> | |
| <button id="musicToggle" class="music-signature__toggle" type="button" aria-label="播放背景音乐"> | |
| <svg class="music-signature__icon music-signature__icon--sound" viewBox="0 0 24 24" focusable="false" aria-hidden="true"> | |
| <path class="music-signature__speaker" d="M4.9 10.1v3.8h3.2l4.8 4.2V5.9l-4.8 4.2H4.9Z"></path> | |
| <path class="music-signature__wave music-signature__wave--soft" d="M15.8 9.5c.58.68.92 1.54.92 2.5s-.34 1.82-.92 2.5"></path> | |
| <path class="music-signature__wave music-signature__wave--full" d="M18 7.7c1.02 1.12 1.6 2.58 1.6 4.3s-.58 3.18-1.6 4.3"></path> | |
| <path class="music-signature__mute-line" d="M7 7.15 16.55 16.85"></path> | |
| </svg> | |
| </button> | |
| <p class="music-signature__credit" aria-live="polite"> | |
| <span id="musicTitle">Car 24</span> | |
| <span class="music-signature__separator"> - </span> | |
| <span id="musicArtist">菅野よう子</span> | |
| </p> | |
| <span id="musicVolume" class="music-signature__volume" aria-hidden="true">24%</span> | |
| </div> | |
| <audio id="musicLight" src="public/audio/car24.mp3" preload="metadata" loop></audio> | |
| <audio id="musicDark" src="public/audio/road-to-the-west.mp3" preload="metadata" loop></audio> | |
| <svg id="mapSvg" class="map-svg" viewBox="0 0 1440 920" preserveAspectRatio="xMidYMid slice" role="img" aria-label="世界球体地图"> | |
| <defs> | |
| <radialGradient id="globeWash" cx="34%" cy="28%" r="74%"> | |
| <stop class="ocean-stop-a" offset="0%" stop-opacity="1" /> | |
| <stop class="ocean-stop-b" offset="48%" stop-opacity="0.96" /> | |
| <stop class="ocean-stop-c" offset="100%" stop-opacity="1" /> | |
| </radialGradient> | |
| <pattern id="oceanWaves" width="224" height="224" patternUnits="userSpaceOnUse"> | |
| <image id="oceanWaveImage" href="public/textures/ocean-wave-lines-alpha.png" width="224" height="224" preserveAspectRatio="none" /> | |
| </pattern> | |
| <pattern id="landTexture" width="260" height="260" patternUnits="userSpaceOnUse"> | |
| <image id="landTextureImage" href="public/textures/land-soft-halftone-dots.png" width="260" height="260" preserveAspectRatio="none" /> | |
| </pattern> | |
| <filter id="cityMapDarkLandMultiply" color-interpolation-filters="sRGB"> | |
| <feColorMatrix in="SourceGraphic" type="saturate" values="0" result="gray" /> | |
| <feComponentTransfer in="gray" result="softGray"> | |
| <feFuncR type="linear" slope="1" intercept="0.012" /> | |
| <feFuncG type="linear" slope="1" intercept="0.012" /> | |
| <feFuncB type="linear" slope="1" intercept="0.012" /> | |
| </feComponentTransfer> | |
| <feFlood flood-color="#548482" flood-opacity="0.66" result="landTint" /> | |
| <feBlend in="softGray" in2="landTint" mode="multiply" result="tintedLand" /> | |
| <feComposite in="tintedLand" in2="SourceAlpha" operator="in" /> | |
| </filter> | |
| </defs> | |
| <g id="globeLayer"></g> | |
| <g id="worldLayer"></g> | |
| <g id="highlightLayer"></g> | |
| <g id="cityLayer"></g> | |
| </svg> | |
| <div id="cityLabelLayer" class="city-label-layer" aria-hidden="true"></div> | |
| <svg id="specialOverlaySvg" class="special-overlay-svg" aria-hidden="true"></svg> | |
| <div id="specialLabelLayer" class="special-label-layer" aria-label="城市特辑入口"></div> | |
| <div id="specialBookLayer" class="special-book-layer" aria-label="城市特辑书籍"></div> | |
| <aside id="cityTooltip" class="book-card is-ticket" aria-live="polite" hidden> | |
| <section class="book-card__ticket-view" aria-label="书票卡片"> | |
| <button class="book-card__close book-card__close--float" type="button" aria-label="关闭书籍卡片">×</button> | |
| <article class="book-ticket"> | |
| <div class="book-ticket__cover" aria-hidden="true"> | |
| <img alt="" /> | |
| <span class="book-card__cover-fallback">BOOK</span> | |
| </div> | |
| <div class="book-ticket__body"> | |
| <p class="book-card__location-zh"></p> | |
| <p class="book-card__location-en"></p> | |
| <h2 class="book-card__title-zh"></h2> | |
| <p class="book-card__title-original"></p> | |
| <p class="book-card__meta"></p> | |
| <p class="book-card__shareline"></p> | |
| <div class="book-card__ticket-actions"> | |
| <button class="book-card__action book-card__save" type="button"> | |
| <svg class="book-card__action-icon" viewBox="0 0 24 24" focusable="false" aria-hidden="true"> | |
| <path d="M20.2 5.8c-1.9-1.9-5-1.8-6.8.2L12 7.5 10.6 6c-1.8-2-4.9-2.1-6.8-.2-2 2-2.1 5.2-.1 7.3L12 21l8.3-7.9c2-2.1 1.9-5.3-.1-7.3Z" /> | |
| </svg> | |
| <span class="book-card__action-label">收藏</span> | |
| </button> | |
| <button class="book-card__action book-card__share" type="button"> | |
| <svg class="book-card__action-icon" viewBox="0 0 24 24" focusable="false" aria-hidden="true"> | |
| <path d="M8.2 12.9 15.8 17" /> | |
| <path d="M15.8 7 8.2 11.1" /> | |
| <circle cx="6" cy="12" r="2.4" /> | |
| <circle cx="18" cy="6" r="2.4" /> | |
| <circle cx="18" cy="18" r="2.4" /> | |
| </svg> | |
| <span class="book-card__action-label">分享</span> | |
| </button> | |
| <button class="book-card__action book-card__detail" type="button"> | |
| <svg class="book-card__action-icon" viewBox="0 0 24 24" focusable="false" aria-hidden="true"> | |
| <path d="M4.5 5.5c2.5-.9 5-.5 7.5 1.2 2.5-1.7 5-2.1 7.5-1.2v13c-2.5-.8-5-.4-7.5 1.2-2.5-1.6-5-2-7.5-1.2v-13Z" /> | |
| <path d="M12 6.7v13" /> | |
| </svg> | |
| <span class="book-card__action-label">详情</span> | |
| </button> | |
| </div> | |
| </div> | |
| </article> | |
| </section> | |
| <section class="book-card__detail-view" aria-label="藏书卡详情"> | |
| <button class="book-card__close book-card__close--detail" type="button" aria-label="关闭书籍卡片">×</button> | |
| <div class="book-card__detail-cover" aria-hidden="true"> | |
| <img alt="" /> | |
| <span class="book-card__cover-fallback">BOOK</span> | |
| </div> | |
| <div class="book-card__detail-body"> | |
| <div class="book-card__detail-top"> | |
| <div> | |
| <p class="book-card__location-zh"></p> | |
| <p class="book-card__location-en"></p> | |
| </div> | |
| </div> | |
| <h2 class="book-card__title-zh"></h2> | |
| <p class="book-card__title-original"></p> | |
| <p class="book-card__meta"></p> | |
| <div class="book-card__intro"></div> | |
| <div class="book-card__detail-footer"> | |
| <div class="book-card__explore"> | |
| <button class="book-card__city-page book-card__city-page--prev" type="button" aria-label="上一座城市">«</button> | |
| <div class="book-card__pager" hidden> | |
| <button class="book-card__page book-card__page--prev" type="button" aria-label="上一本书">‹</button> | |
| <span class="book-card__counter">1 / 1</span> | |
| <button class="book-card__page book-card__page--next" type="button" aria-label="下一本书">›</button> | |
| </div> | |
| <button class="book-card__city-page book-card__city-page--next" type="button" aria-label="下一座城市">»</button> | |
| </div> | |
| <div class="book-card__detail-actions"> | |
| <button class="book-card__action book-card__save" type="button"> | |
| <svg class="book-card__action-icon" viewBox="0 0 24 24" focusable="false" aria-hidden="true"> | |
| <path d="M20.2 5.8c-1.9-1.9-5-1.8-6.8.2L12 7.5 10.6 6c-1.8-2-4.9-2.1-6.8-.2-2 2-2.1 5.2-.1 7.3L12 21l8.3-7.9c2-2.1 1.9-5.3-.1-7.3Z" /> | |
| </svg> | |
| <span class="book-card__action-label">收藏</span> | |
| </button> | |
| <button class="book-card__action book-card__share" type="button"> | |
| <svg class="book-card__action-icon" viewBox="0 0 24 24" focusable="false" aria-hidden="true"> | |
| <path d="M8.2 12.9 15.8 17" /> | |
| <path d="M15.8 7 8.2 11.1" /> | |
| <circle cx="6" cy="12" r="2.4" /> | |
| <circle cx="18" cy="6" r="2.4" /> | |
| <circle cx="18" cy="18" r="2.4" /> | |
| </svg> | |
| <span class="book-card__action-label">分享</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </aside> | |
| <div id="sharePreview" class="share-preview" hidden> | |
| <section class="share-preview__canvas" aria-label="分享书票预览"> | |
| <div class="share-preview__topbar"> | |
| <p class="share-preview__eyebrow">分享书票预览</p> | |
| <button class="share-preview__close" type="button" aria-label="关闭分享预览">×</button> | |
| </div> | |
| <div class="share-ticket-shell"> | |
| <article class="share-ticket"> | |
| <div class="share-ticket__cover" aria-hidden="true"> | |
| <img alt="" /> | |
| <span class="book-card__cover-fallback">BOOK</span> | |
| </div> | |
| <div class="share-ticket__body"> | |
| <p class="book-card__location-zh"></p> | |
| <p class="book-card__location-en"></p> | |
| <h2 class="book-card__title-zh"></h2> | |
| <p class="book-card__title-original"></p> | |
| <p class="book-card__meta"></p> | |
| <blockquote class="book-card__quote"></blockquote> | |
| <div class="share-ticket__stamp"> | |
| <span class="share-ticket__brand">World Citybook</span> | |
| <img class="share-ticket__seal" src="public/assets/stamps/city-routebook-seal.png" alt="" /> | |
| </div> | |
| </div> | |
| </article> | |
| </div> | |
| <button class="share-preview__page share-preview__page--prev" type="button" aria-label="上一张分享书票">‹‹</button> | |
| <button class="share-preview__page share-preview__page--next" type="button" aria-label="下一张分享书票">››</button> | |
| <div class="share-preview__actions"> | |
| <button class="share-preview__save" type="button">保存藏书票</button> | |
| </div> | |
| </section> | |
| </div> | |
| <section id="bookshelfView" class="bookshelf-view" aria-label="我的书架" hidden> | |
| <button id="bookshelfClose" class="special-back-button bookshelf-back-button" type="button" aria-label="返回全球视角"> | |
| <span class="special-back-button__icon" aria-hidden="true"> | |
| <svg viewBox="0 0 24 24" focusable="false"> | |
| <circle cx="12" cy="12" r="9.25"></circle> | |
| <path d="M2.75 12h18.5M12 2.75a13.8 13.8 0 0 1 0 18.5M12 2.75a13.8 13.8 0 0 0 0 18.5"></path> | |
| </svg> | |
| </span> | |
| <span class="special-back-button__label">返回全球视角</span> | |
| </button> | |
| <div class="bookshelf-view__stage"> | |
| <div id="bookshelfGrid" class="bookshelf-grid" aria-live="polite"></div> | |
| <p id="bookshelfEmpty" class="bookshelf-empty">收藏一本城市之书,它会出现在这里。</p> | |
| </div> | |
| <div id="bookshelfPager" class="bookshelf-pager" aria-label="书架翻页" hidden> | |
| <button id="bookshelfPrev" class="book-card__page book-card__page--prev bookshelf-pager__button" type="button" aria-label="上一页">‹</button> | |
| <span id="bookshelfPageLabel" class="book-card__counter bookshelf-pager__label">1 / 1</span> | |
| <button id="bookshelfNext" class="book-card__page book-card__page--next bookshelf-pager__button" type="button" aria-label="下一页">›</button> | |
| </div> | |
| </section> | |
| </section> | |
| </main> | |
| <script> | |
| window.CITYBOOK_MAP_CONFIG = { | |
| cityTileProvider: "cartoLight", | |
| defaultMapStyleId: "light", | |
| mapboxStyleId: "mapbox/light-v11", | |
| mapboxAccessToken: "", | |
| retinaTiles: true, | |
| }; | |
| </script> | |
| <script src="public/vendor/d3-array-3.2.4.min.js?v=20260714-predeploy-010"></script> | |
| <script src="public/vendor/d3-geo-3.1.1.min.js?v=20260714-predeploy-010"></script> | |
| <script type="module" src="src/app.js?v=20260714-predeploy-010"></script> | |
| </body> | |
| </html> | |