feat: update center route-preview and align buttons
This commit is contained in:
@@ -5,5 +5,5 @@ export const STATION_OUTLINE_WIDTH = 4;
|
|||||||
export const SIGHT_SIZE = 40;
|
export const SIGHT_SIZE = 40;
|
||||||
export const SCALE_FACTOR = 50;
|
export const SCALE_FACTOR = 50;
|
||||||
|
|
||||||
export const BACKGROUND_COLOR = 0x111111;
|
export const BACKGROUND_COLOR = 0x000;
|
||||||
export const PATH_COLOR = 0xff4d4d;
|
export const PATH_COLOR = 0xff4d4d;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|||||||
import { flushSync } from "react-dom";
|
import { flushSync } from "react-dom";
|
||||||
import type { PointerEvent as ReactPointerEvent, CSSProperties } from "react";
|
import type { PointerEvent as ReactPointerEvent, CSSProperties } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
import { useMapData } from "../MapDataContext";
|
import { useMapData } from "../MapDataContext";
|
||||||
|
import { AlignLeft, AlignCenter, AlignRight } from "lucide-react";
|
||||||
import { useTransform } from "../TransformContext";
|
import { useTransform } from "../TransformContext";
|
||||||
import { coordinatesToLocal, localToCoordinates } from "../utils";
|
import { coordinatesToLocal, localToCoordinates } from "../utils";
|
||||||
import { BACKGROUND_COLOR, SCALE_FACTOR, UP_SCALE } from "../Constants";
|
import { BACKGROUND_COLOR, SCALE_FACTOR, UP_SCALE } from "../Constants";
|
||||||
@@ -12,6 +12,16 @@ import { SightData } from "../types";
|
|||||||
|
|
||||||
const SIGHT_ICON_URL = "/sight_icon.svg";
|
const SIGHT_ICON_URL = "/sight_icon.svg";
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{ label: <AlignLeft size={16} />, value: 1, align: "left" },
|
||||||
|
{
|
||||||
|
label: <AlignCenter size={16} />,
|
||||||
|
value: 2,
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
{ label: <AlignRight size={16} />, value: 3, align: "right" },
|
||||||
|
];
|
||||||
|
|
||||||
type Vec2 = { x: number; y: number };
|
type Vec2 = { x: number; y: number };
|
||||||
|
|
||||||
type Transform = {
|
type Transform = {
|
||||||
@@ -412,6 +422,7 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
y: centerY - worldCenterY * clampedScale,
|
y: centerY - worldCenterY * clampedScale,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
lastTransformRef.current = adjusted;
|
lastTransformRef.current = adjusted;
|
||||||
return adjusted;
|
return adjusted;
|
||||||
}, []);
|
}, []);
|
||||||
@@ -534,9 +545,12 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const roundedLat = Math.round(latitude * 1e6) / 1e6;
|
||||||
|
const roundedLon = Math.round(longitude * 1e6) / 1e6;
|
||||||
|
|
||||||
lastCenterRef.current = {
|
lastCenterRef.current = {
|
||||||
latitude: Math.round(latitude * 1e6) / 1e6,
|
latitude: roundedLat,
|
||||||
longitude: Math.round(longitude * 1e6) / 1e6,
|
longitude: roundedLon,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[rotationAngle]
|
[rotationAngle]
|
||||||
@@ -634,8 +648,28 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
}, [cancelScheduledCenterCommit]);
|
}, [cancelScheduledCenterCommit]);
|
||||||
|
|
||||||
const updateTransform = useCallback(
|
const updateTransform = useCallback(
|
||||||
(next: Transform, options?: { immediate?: boolean }) => {
|
(
|
||||||
const adjusted = clampTransformScale(next);
|
next: Transform,
|
||||||
|
options?: { immediate?: boolean; skipClamp?: boolean }
|
||||||
|
) => {
|
||||||
|
console.log("🔄 updateTransform вызван", {
|
||||||
|
inputTransform: next,
|
||||||
|
options,
|
||||||
|
stackTrace: new Error().stack?.split("\n").slice(1, 4).join("\n"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const adjusted = options?.skipClamp ? next : clampTransformScale(next);
|
||||||
|
|
||||||
|
if (adjusted !== next && !options?.skipClamp) {
|
||||||
|
console.log(
|
||||||
|
"🔄 updateTransform: transform изменен после clampTransformScale",
|
||||||
|
{
|
||||||
|
before: next,
|
||||||
|
after: adjusted,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
transformRef.current = adjusted;
|
transformRef.current = adjusted;
|
||||||
if (options?.immediate) {
|
if (options?.immediate) {
|
||||||
flushSync(() => {
|
flushSync(() => {
|
||||||
@@ -1122,6 +1156,10 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
|
|
||||||
let transform = transformRef.current;
|
let transform = transformRef.current;
|
||||||
if (!transform || !Number.isFinite(transform.scale)) {
|
if (!transform || !Number.isFinite(transform.scale)) {
|
||||||
|
if (canvasSize.width === 0 || canvasSize.height === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
transform = computeViewTransform(
|
transform = computeViewTransform(
|
||||||
fallbackVertices,
|
fallbackVertices,
|
||||||
canvas.width,
|
canvas.width,
|
||||||
@@ -1166,9 +1204,25 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
latitude: centerLat as number,
|
latitude: centerLat as number,
|
||||||
longitude: centerLon as number,
|
longitude: centerLon as number,
|
||||||
};
|
};
|
||||||
|
const clamped = clampTransformScale(transform);
|
||||||
|
if (clamped.scale !== transform.scale) {
|
||||||
|
const clampedScale = clamped.scale;
|
||||||
|
transform = {
|
||||||
|
scale: clampedScale,
|
||||||
|
translation: {
|
||||||
|
x: canvas.width / 2 - rotatedX * clampedScale,
|
||||||
|
y: canvas.height / 2 - rotatedY * clampedScale,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
updateTransform(transform, { skipClamp: true });
|
||||||
|
} else {
|
||||||
|
updateTransform(clamped, { skipClamp: true });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
transform = clampTransformScale(transform);
|
||||||
|
updateTransform(transform);
|
||||||
}
|
}
|
||||||
transform = clampTransformScale(transform);
|
|
||||||
updateTransform(transform);
|
|
||||||
} else {
|
} else {
|
||||||
const clamped = clampTransformScale(transform);
|
const clamped = clampTransformScale(transform);
|
||||||
if (clamped !== transform) {
|
if (clamped !== transform) {
|
||||||
@@ -1886,6 +1940,14 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
? { right: 0, transform: "none" }
|
? { right: 0, transform: "none" }
|
||||||
: { left: "50%", transform: "translateX(-50%)" };
|
: { left: "50%", transform: "translateX(-50%)" };
|
||||||
|
|
||||||
|
const secondaryLineHeight = 1.2;
|
||||||
|
const secondaryHeight = showSecondary
|
||||||
|
? secondaryFontSize * secondaryLineHeight
|
||||||
|
: 0;
|
||||||
|
const menuPaddingTop = showSecondary
|
||||||
|
? Math.max(0, secondaryHeight - secondaryMarginTop) + 3
|
||||||
|
: 3;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={station.id}
|
key={station.id}
|
||||||
@@ -1959,6 +2021,7 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
marginTop: -1 * secondaryMarginTop,
|
marginTop: -1 * secondaryMarginTop,
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
fontSize: secondaryFontSize,
|
fontSize: secondaryFontSize,
|
||||||
|
lineHeight: secondaryLineHeight,
|
||||||
color: "#CBCBCB",
|
color: "#CBCBCB",
|
||||||
textShadow: "0 0 3px rgba(0,0,0,0.4)",
|
textShadow: "0 0 3px rgba(0,0,0,0.4)",
|
||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
@@ -1972,6 +2035,65 @@ export const WebGLRouteMapPrototype = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{hoveredStationId === station.id && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "100%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translateX(-50%)",
|
||||||
|
paddingTop: menuPaddingTop,
|
||||||
|
pointerEvents: "auto",
|
||||||
|
zIndex: 10,
|
||||||
|
cursor: "default",
|
||||||
|
}}
|
||||||
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
gap: 4,
|
||||||
|
padding: 4,
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: "white",
|
||||||
|
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{buttons.map((btn) => (
|
||||||
|
<div
|
||||||
|
key={btn.value}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setStationAlignments((prev) => {
|
||||||
|
const next = new Map(prev);
|
||||||
|
next.set(
|
||||||
|
station.id,
|
||||||
|
btn.align as StationAlignment
|
||||||
|
);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setStationAlign(station.id, btn.value);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
padding: "4px 8px",
|
||||||
|
fontSize: 12,
|
||||||
|
cursor: "pointer",
|
||||||
|
backgroundColor:
|
||||||
|
alignment === btn.align
|
||||||
|
? "#e0e0e0"
|
||||||
|
: "transparent",
|
||||||
|
borderRadius: 4,
|
||||||
|
color: "black",
|
||||||
|
fontWeight: 500,
|
||||||
|
userSelect: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{btn.label}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user