From 5481d264e03bacd17f62e7eec7148f7dab54ed91 Mon Sep 17 00:00:00 2001 From: itoshi Date: Thu, 27 Nov 2025 10:58:51 +0300 Subject: [PATCH] feat: update center route-preview and align buttons --- src/pages/Route/route-preview/Constants.ts | 2 +- .../WebGLRouteMapPrototype.tsx | 136 +++++++++++++++++- 2 files changed, 130 insertions(+), 8 deletions(-) diff --git a/src/pages/Route/route-preview/Constants.ts b/src/pages/Route/route-preview/Constants.ts index 1c4add7..498a2ae 100644 --- a/src/pages/Route/route-preview/Constants.ts +++ b/src/pages/Route/route-preview/Constants.ts @@ -5,5 +5,5 @@ export const STATION_OUTLINE_WIDTH = 4; export const SIGHT_SIZE = 40; export const SCALE_FACTOR = 50; -export const BACKGROUND_COLOR = 0x111111; +export const BACKGROUND_COLOR = 0x000; export const PATH_COLOR = 0xff4d4d; diff --git a/src/pages/Route/route-preview/webgl-prototype/WebGLRouteMapPrototype.tsx b/src/pages/Route/route-preview/webgl-prototype/WebGLRouteMapPrototype.tsx index 8db9dcf..b9b2c32 100644 --- a/src/pages/Route/route-preview/webgl-prototype/WebGLRouteMapPrototype.tsx +++ b/src/pages/Route/route-preview/webgl-prototype/WebGLRouteMapPrototype.tsx @@ -2,8 +2,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { flushSync } from "react-dom"; import type { PointerEvent as ReactPointerEvent, CSSProperties } from "react"; import { observer } from "mobx-react-lite"; - import { useMapData } from "../MapDataContext"; +import { AlignLeft, AlignCenter, AlignRight } from "lucide-react"; import { useTransform } from "../TransformContext"; import { coordinatesToLocal, localToCoordinates } from "../utils"; 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 buttons = [ + { label: , value: 1, align: "left" }, + { + label: , + value: 2, + align: "center", + }, + { label: , value: 3, align: "right" }, +]; + type Vec2 = { x: number; y: number }; type Transform = { @@ -412,6 +422,7 @@ export const WebGLRouteMapPrototype = observer(() => { y: centerY - worldCenterY * clampedScale, }, }; + lastTransformRef.current = adjusted; return adjusted; }, []); @@ -534,9 +545,12 @@ export const WebGLRouteMapPrototype = observer(() => { return; } + const roundedLat = Math.round(latitude * 1e6) / 1e6; + const roundedLon = Math.round(longitude * 1e6) / 1e6; + lastCenterRef.current = { - latitude: Math.round(latitude * 1e6) / 1e6, - longitude: Math.round(longitude * 1e6) / 1e6, + latitude: roundedLat, + longitude: roundedLon, }; }, [rotationAngle] @@ -634,8 +648,28 @@ export const WebGLRouteMapPrototype = observer(() => { }, [cancelScheduledCenterCommit]); 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; if (options?.immediate) { flushSync(() => { @@ -1122,6 +1156,10 @@ export const WebGLRouteMapPrototype = observer(() => { let transform = transformRef.current; if (!transform || !Number.isFinite(transform.scale)) { + if (canvasSize.width === 0 || canvasSize.height === 0) { + return; + } + transform = computeViewTransform( fallbackVertices, canvas.width, @@ -1166,9 +1204,25 @@ export const WebGLRouteMapPrototype = observer(() => { latitude: centerLat 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 { const clamped = clampTransformScale(transform); if (clamped !== transform) { @@ -1886,6 +1940,14 @@ export const WebGLRouteMapPrototype = observer(() => { ? { right: 0, transform: "none" } : { 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 (
{ marginTop: -1 * secondaryMarginTop, fontWeight: 400, fontSize: secondaryFontSize, + lineHeight: secondaryLineHeight, color: "#CBCBCB", textShadow: "0 0 3px rgba(0,0,0,0.4)", whiteSpace: "nowrap", @@ -1972,6 +2035,65 @@ export const WebGLRouteMapPrototype = observer(() => {
+ {hoveredStationId === station.id && ( +
e.stopPropagation()} + > +
+ {buttons.map((btn) => ( +
{ + 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} +
+ ))} +
+
+ )} ); })}