Spaces:
Running
Running
| export const CITY_MAP_BOOK_TOP_FORBIDDEN_BOTTOM = 32; | |
| export const CITY_MAP_BOOK_BOTTOM_FORBIDDEN_TOP_OFFSET = 56; | |
| export const CITY_MAP_BOOK_SAFE_HEIGHT = 264; | |
| export const CITY_MAP_BOOK_HOVER_SAFE_SCALE = 1.055; | |
| export const CITY_MAP_BOOK_FORBIDDEN_GAP = 2; | |
| export const CITY_MAP_SPECIAL_BOOK_LAYOUTS = { | |
| shanghai: { | |
| minWidth: 1200, | |
| marginX: 64, | |
| marginY: 132, | |
| slots: { | |
| "chn-shanghai--douban-25924323": { x: 0.1143, y: 0.4341 }, | |
| "chn-shanghai--manual-xinhualu": { x: 0.2254, y: 0.2208 }, | |
| "chn-shanghai--map-shanghai-1947-hanghao": { x: 0.3516, y: 0.2208 }, | |
| "chn-shanghai--douban-35299337": { x: 0.478, y: 0.18 }, | |
| "chn-shanghai--douban-20495120": { x: 0.6006, y: 0.2208 }, | |
| "chn-shanghai--manual-jiangnan-shanghai-gardens": { x: 0.7253, y: 0.2208 }, | |
| "chn-shanghai--douban-35408305": { x: 0.8568, y: 0.3114 }, | |
| "chn-shanghai--douban-36362358": { x: 0.2703, y: 0.7578, offsetY: 5 }, | |
| "chn-shanghai--douban-37442766": { x: 0.3944, y: 0.8185 }, | |
| "chn-shanghai--douban-36952719": { x: 0.6121, y: 0.8158 }, | |
| "chn-shanghai--douban-30254263": { x: 0.7253, y: 0.8168 }, | |
| "chn-shanghai--douban-26849413": { x: 0.8371, y: 0.7864, offsetY: -25 }, | |
| "chn-shanghai--douban-7055710": { x: 0.9499, y: 0.6581, offsetX: -5 }, | |
| }, | |
| }, | |
| beijing: { | |
| minWidth: 1200, | |
| marginX: 88, | |
| marginY: 132, | |
| safeHeight: 264, | |
| hoverSafeScale: CITY_MAP_BOOK_HOVER_SAFE_SCALE, | |
| topForbiddenBottom: 32, | |
| topSafeGap: CITY_MAP_BOOK_FORBIDDEN_GAP, | |
| bottomForbiddenTopOffset: 66, | |
| slots: { | |
| "chn-beijing--douban-1070711": { x: 0.075, y: 0.2786 }, | |
| "chn-beijing--douban-36159853": { x: 0.1964, y: 0.2521 }, | |
| "chn-beijing--homeshop-wear": { x: 0.3182, y: 0.2396 }, | |
| "chn-beijing--douban-37508268": { x: 0.4393, y: 0.322 }, | |
| "chn-beijing--douban-25751679": { x: 0.5604, y: 0.2869 }, | |
| "chn-beijing--douban-37131044": { x: 0.6817, y: 0.2744 }, | |
| "chn-beijing--douban-35279673": { x: 0.8036, y: 0.2883 }, | |
| "chn-beijing--douban-34995523": { x: 0.92, y: 0.3858, offsetY: -40 }, | |
| "chn-beijing--douban-30116399": { x: 0.075, y: 0.6643 }, | |
| "chn-beijing--douban-33395164": { x: 0.22, y: 0.7893 }, | |
| "chn-beijing--douban-30469133": { x: 0.36, y: 0.8257 }, | |
| "chn-beijing--douban-34991723": { x: 0.5, y: 0.8186 }, | |
| "chn-beijing--douban-37134106": { x: 0.64, y: 0.8247 }, | |
| "chn-beijing--isbn-9787540265052": { x: 0.7798, y: 0.7893 }, | |
| "chn-beijing--douban-26580056": { x: 0.92, y: 0.7893 }, | |
| }, | |
| }, | |
| tokyo: { | |
| minWidth: 1200, | |
| marginX: 88, | |
| marginY: 132, | |
| safeHeight: 264, | |
| hoverSafeScale: CITY_MAP_BOOK_HOVER_SAFE_SCALE, | |
| topForbiddenBottom: 32, | |
| topSafeGap: CITY_MAP_BOOK_FORBIDDEN_GAP, | |
| bottomForbiddenTopOffset: 66, | |
| slots: { | |
| "jpn-tokyo--douban-35928183": { x: 0.1508, y: 0.2744 }, | |
| "jpn-tokyo--douban-30438342": { x: 0.262, y: 0.2396 }, | |
| "jpn-tokyo--external-tsukuda-tsukishima-magazine": { x: 0.3831, y: 0.2396 }, | |
| "jpn-tokyo--map-qchizu-tokyo-3000": { x: 0.4952, y: 0.2396 }, | |
| "jpn-tokyo--douban-36448234": { x: 0.6385, y: 0.2396 }, | |
| "jpn-tokyo--douban-24720958": { x: 0.7691, y: 0.2883 }, | |
| "jpn-tokyo--douban-26819477": { x: 0.8639, y: 0.2883 }, | |
| "jpn-tokyo--douban-34950394": { x: 0.1446, y: 0.7503 }, | |
| "jpn-tokyo--douban-26945179": { x: 0.2573, y: 0.8134 }, | |
| "jpn-tokyo--douban-36096287": { x: 0.3745, y: 0.8069 }, | |
| "jpn-tokyo--douban-37090707": { x: 0.6924, y: 0.8045 }, | |
| "jpn-tokyo--douban-2389079": { x: 0.8056, y: 0.7337 }, | |
| "jpn-tokyo--douban-34663226": { x: 0.5703, y: 0.7995 }, | |
| }, | |
| }, | |
| "new-york": { | |
| minWidth: 1200, | |
| marginX: 88, | |
| marginY: 132, | |
| safeHeight: 245, | |
| slots: { | |
| "usa-new-york-city--douban-37199094": { x: 0.0747, y: 0.273 }, | |
| "usa-new-york-city--douban-36889885": { x: 0.1885, y: 0.2451 }, | |
| "usa-new-york-city--douban-35687602": { x: 0.3022, y: 0.273 }, | |
| "usa-new-york-city--map-mapping-historical-ny": { x: 0.4299, y: 0.2228 }, | |
| "usa-new-york-city--douban-37281194": { x: 0.6118, y: 0.2646 }, | |
| "usa-new-york-city--douban-30201329": { x: 0.086, y: 0.7159 }, | |
| "usa-new-york-city--douban-6124372": { x: 0.205, y: 0.7368 }, | |
| "usa-new-york-city--douban-30441545": { x: 0.3241, y: 0.7159 }, | |
| "usa-new-york-city--external-celluloid-skyline": { x: 0.4828, y: 0.7368 }, | |
| "usa-new-york-city--douban-35013441": { x: 0.5952, y: 0.7159 }, | |
| "usa-new-york-city--douban-35740229": { x: 0.7077, y: 0.7368 }, | |
| "usa-new-york-city--douban-26818878": { x: 0.8201, y: 0.7159 }, | |
| "usa-new-york-city--douban-1913467": { x: 0.9325, y: 0.7368 }, | |
| "usa-new-york-city--douban-27037182": { x: 0.787, y: 0.3203 }, | |
| }, | |
| }, | |
| "hong-kong": { | |
| minWidth: 1200, | |
| marginX: 88, | |
| marginY: 132, | |
| slots: { | |
| "chn-hong-kong--douban-36024491": { x: 0.119, y: 0.2354, offsetX: 40 }, | |
| "chn-hong-kong--manual-zhenzhong-hk": { x: 0.2639, y: 0.2549, offsetX: 40 }, | |
| "chn-hong-kong--manual-hong-kong-guide-2014": { x: 0.4944, y: 0.18, offsetX: -40, offsetY: -10 }, | |
| "chn-hong-kong--douban-27004436": { x: 0.69, y: 0.357, offsetY: -60 }, | |
| "chn-hong-kong--manual-power-generation-urban-renewal": { x: 0.805, y: 0.357, offsetY: -60 }, | |
| "chn-hong-kong--douban-30279653": { x: 0.075, y: 0.66 }, | |
| "chn-hong-kong--douban-35452941": { x: 0.1992, y: 0.765 }, | |
| "chn-hong-kong--manual-diwenzhi": { x: 0.3233, y: 0.765 }, | |
| "chn-hong-kong--douban-36285315": { x: 0.4475, y: 0.765 }, | |
| "chn-hong-kong--manual-xiangtu-hongkong": { x: 0.5717, y: 0.765 }, | |
| "chn-hong-kong--external-sham-shui-po-101": { x: 0.6958, y: 0.765 }, | |
| "chn-hong-kong--external-wanchai-wak-dongnin": { x: 0.82, y: 0.715 }, | |
| "chn-hong-kong--douban-27136081": { x: 0.92, y: 0.385 }, | |
| }, | |
| }, | |
| }; | |
| export function hasCompleteCityMapSpecialBookLayout(special, entries = []) { | |
| const layout = CITY_MAP_SPECIAL_BOOK_LAYOUTS[special?.id]; | |
| if (!layout || !entries.length) return false; | |
| return entries.every((entry) => Boolean(layout.slots?.[entry?.book?.id])); | |
| } | |
| export function applyCityMapSpecialBookLayout(special, entries, points, stageRect) { | |
| const layout = CITY_MAP_SPECIAL_BOOK_LAYOUTS[special?.id]; | |
| if (!layout || stageRect.width < (layout.minWidth || 0)) return points; | |
| const minCenterY = cityMapBookMinCenterY(layout); | |
| const maxCenterY = cityMapBookMaxCenterY(layout, stageRect, minCenterY); | |
| return points.map((point, index) => { | |
| const bookId = entries[index]?.book?.id; | |
| const slot = layout.slots[bookId]; | |
| if (!slot) return point; | |
| const marginX = layout.marginX ?? 72; | |
| const marginY = layout.marginY ?? 96; | |
| return { | |
| ...point, | |
| x: clamp(stageRect.width * slot.x, marginX, Math.max(marginX, stageRect.width - marginX)) + (slot.offsetX ?? 0), | |
| y: clamp(stageRect.height * slot.y, Math.max(marginY, minCenterY), maxCenterY) + (slot.offsetY ?? 0), | |
| }; | |
| }); | |
| } | |
| export function cityMapBookTopForbiddenBottom(special) { | |
| const layout = CITY_MAP_SPECIAL_BOOK_LAYOUTS[special?.id]; | |
| return layout?.topForbiddenBottom ?? CITY_MAP_BOOK_TOP_FORBIDDEN_BOTTOM; | |
| } | |
| function cityMapBookMinCenterY(layout) { | |
| const topForbiddenBottom = layout.topForbiddenBottom ?? CITY_MAP_BOOK_TOP_FORBIDDEN_BOTTOM; | |
| const safeHeight = layout.safeHeight ?? CITY_MAP_BOOK_SAFE_HEIGHT; | |
| const hoverSafeScale = layout.hoverSafeScale ?? 1; | |
| const topSafeGap = layout.topSafeGap ?? 0; | |
| return topForbiddenBottom + (safeHeight * hoverSafeScale) / 2 + topSafeGap; | |
| } | |
| function cityMapBookMaxCenterY(layout, stageRect, minCenterY) { | |
| const bottomOffset = layout.bottomForbiddenTopOffset ?? CITY_MAP_BOOK_BOTTOM_FORBIDDEN_TOP_OFFSET; | |
| const safeHeight = layout.safeHeight ?? CITY_MAP_BOOK_SAFE_HEIGHT; | |
| const bottomForbiddenTop = stageRect.height - bottomOffset; | |
| return Math.max(minCenterY, bottomForbiddenTop - safeHeight / 2); | |
| } | |
| function clamp(value, min, max) { | |
| return Math.min(Math.max(value, min), max); | |
| } | |