优化:地图展示效果

This commit is contained in:
taojiacheng committed 2026-07-23 13:37:41 +08:00
1 parent 77bb7b1513
commit 8277c0459e
2 files changed
+82 -50

No files matched your search

@@ -35,6 +35,31 @@ interface MapControlButtonProps extends Pick<
children: ReactNode;
}
const MAP_LABEL_STYLE = {
textShadow:
'-1px -1px 0 rgba(250, 245, 230, 0.95), 1px -1px 0 rgba(250, 245, 230, 0.95), -1px 1px 0 rgba(250, 245, 230, 0.95), 1px 1px 0 rgba(250, 245, 230, 0.95), 0 2px 4px rgba(26, 20, 15, 0.35)',
} as const;
const AVAILABLE_MARKER_STYLE = {
background:
'radial-gradient(circle at 30% 22%, rgba(255,235,229,0.96) 0%, rgba(255,210,198,0.56) 9%, transparent 27%), linear-gradient(145deg, #d86b5c 0%, #a93e34 34%, #76221e 68%, #4a110f 100%)',
boxShadow:
'inset 0 1px 1px rgba(255,255,255,0.46), inset 0 -2px 3px rgba(46,8,6,0.48), 0 1px 3px rgba(26,20,15,0.42)',
} as const;
const SELECTED_MARKER_STYLE = {
...AVAILABLE_MARKER_STYLE,
boxShadow:
'inset 0 1px 1px rgba(255,255,255,0.48), inset 0 -2px 3px rgba(46,8,6,0.42), 0 0 0 2px rgba(250,245,230,0.88), 0 0 0 4px rgba(145,36,28,0.3), 0 2px 5px rgba(26,20,15,0.4)',
} as const;
const LOCKED_MARKER_STYLE = {
background:
'radial-gradient(circle at 32% 26%, #8c8177 0%, #5f554d 38%, #332b27 100%)',
boxShadow:
'inset 0 1px 1px rgba(255,255,255,0.22), inset 0 -2px 3px rgba(12,9,7,0.42), 0 1px 3px rgba(26,20,15,0.36)',
} as const;
function MapControlButton({ label, children, onClick }: MapControlButtonProps) {
return (
<button
@@ -103,7 +128,7 @@ function LockIcon() {
return (
<svg
viewBox="0 0 20 20"
className="size-3.5"
className="size-2.5"
aria-hidden="true"
fill="none"
stroke="currentColor"
@@ -133,6 +158,41 @@ function CloseIcon() {
);
}
function FacilityMarkerGlyph({
locked,
selected = false,
}: {
locked: boolean;
selected?: boolean;
}) {
return (
<span
aria-hidden="true"
style={
locked
? LOCKED_MARKER_STYLE
: selected
? SELECTED_MARKER_STYLE
: AVAILABLE_MARKER_STYLE
}
className={cn(
'relative flex size-[18px] shrink-0 transform-gpu items-center justify-center rounded-full border transition duration-150',
locked
? 'border-paper/30 text-paper/75 group-hover:border-paper/45 group-hover:scale-105'
: selected
? 'border-paper/90 text-paper'
: 'border-paper/65 text-paper/95 group-hover:border-paper/90 group-hover:scale-105',
)}
>
{locked ? (
<span className="relative z-10 flex items-center justify-center">
<LockIcon />
</span>
) : null}
</span>
);
}
function getHotspotState(
spot: SectMapHotspot,
facilities: ReadonlyMap<string, SectFacilityState>,
@@ -220,10 +280,9 @@ export function SectMap({
<TransformWrapper
ref={transformRef}
initialScale={1}
minScale={0.72}
minScale={1}
maxScale={3}
centerOnInit
centerZoomedOut
limitToBounds
wheel={{ step: 0.16 }}
panning={{
@@ -304,41 +363,21 @@ export function SectMap({
aria-label={`${spot.label}${level ? `,${level}级` : ''}${state.locked ? ',未开放' : ''}`}
onClick={() => setSelectedId(spot.id)}
className={cn(
'sect-map-marker group focus-visible:outline-crimson relative flex size-11 items-center justify-center rounded-full focus-visible:outline-2 focus-visible:outline-offset-2',
'sect-map-marker group focus-visible:outline-crimson relative flex size-11 transform-gpu items-center justify-center rounded-full transition-transform duration-150 focus-visible:outline-2 focus-visible:outline-offset-2',
selected ? 'z-20 scale-[1.08]' : '',
)}
>
<span
aria-hidden="true"
className={cn(
'absolute size-7 rotate-45 rounded-[3px] border shadow-[0_2px_8px_rgba(26,20,15,0.18)] backdrop-blur-[1px] transition',
state.locked
? 'border-paper/30 bg-ink/65 text-paper/80'
: selected
? 'border-crimson bg-paper text-crimson shadow-[0_3px_12px_rgba(118,32,24,0.25)]'
: 'border-ink/35 bg-paper/75 text-ink-secondary hover:border-crimson/60 hover:bg-paper',
)}
<FacilityMarkerGlyph
locked={state.locked}
selected={selected}
/>
<span
aria-hidden="true"
style={MAP_LABEL_STYLE}
className={cn(
'relative z-10 flex items-center justify-center',
'pointer-events-none absolute top-[34px] left-1/2 z-20 -translate-x-1/2 px-1 py-0.5 text-[11px] leading-4 font-semibold tracking-[0.08em] whitespace-nowrap',
state.locked
? 'text-paper/75'
: 'text-crimson/80',
)}
>
{state.locked ? (
<LockIcon />
) : (
<span className="block size-1.5 rounded-full bg-current" />
)}
</span>
<span
className={cn(
'bg-paper/92 text-ink pointer-events-none absolute top-10 left-1/2 z-20 -translate-x-1/2 border-b border-dashed px-2 py-1 text-xs leading-5 whitespace-nowrap shadow-sm transition-opacity',
selected
? 'border-crimson/35 opacity-100'
: 'border-ink/20 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100',
? 'text-ink-secondary'
: 'text-crimson',
)}
>
{spot.label}
@@ -414,25 +453,9 @@ export function SectMap({
key={spot.id}
type="button"
onClick={() => selectFromList(spot)}
className="hover:bg-ink/5 focus-visible:outline-crimson flex w-full items-center gap-3 px-1 py-3 text-left transition-colors focus-visible:outline-2 focus-visible:outline-offset-2"
className="hover:bg-ink/5 focus-visible:outline-crimson group flex w-full items-center gap-3 px-1 py-3 text-left transition-colors focus-visible:outline-2 focus-visible:outline-offset-2"
>
<span
aria-hidden="true"
className={cn(
'flex size-7 shrink-0 rotate-45 items-center justify-center rounded-[3px] border',
state.locked
? 'border-ink/25 bg-ink/55 text-paper/80'
: 'border-crimson/25 bg-paper text-crimson/75',
)}
>
<span className="-rotate-45">
{state.locked ? (
<LockIcon />
) : (
<span className="block size-1.5 rounded-full bg-current" />
)}
</span>
</span>
<FacilityMarkerGlyph locked={state.locked} />
<span className="min-w-0 flex-1">
<span className="flex flex-wrap items-baseline gap-x-2">
<strong className="text-sm">{spot.label}</strong>
@@ -56,6 +56,15 @@ describe('sect map configuration', () => {
expect(mapSource).toContain('KeepScale');
expect(mapSource).toContain('设施名录');
expect(mapSource).toContain('aria-pressed={selected}');
expect(mapSource).toContain('minScale={1}');
expect(mapSource).not.toContain('centerZoomedOut');
expect(mapSource).toContain('FacilityMarkerGlyph');
expect(mapSource).not.toContain('rotate-45');
expect(mapSource).toContain('size-[18px]');
expect(mapSource).toContain('scale-[1.08]');
expect(mapSource).toContain('AVAILABLE_MARKER_STYLE');
expect(mapSource).not.toContain('rounded-full bg-current');
expect(mapSource).not.toContain('opacity-0 group-hover:opacity-100');
expect(mapSource).not.toContain('disabled={state.locked}');
});