fix: Update map with tables fixes
This commit is contained in:
@ -1,4 +1,8 @@
|
||||
import { FederatedMouseEvent, Graphics } from "pixi.js";
|
||||
import { useCallback, useState, useEffect, useRef, FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
// --- Заглушки для зависимостей (замените на ваши реальные импорты) ---
|
||||
import {
|
||||
BACKGROUND_COLOR,
|
||||
PATH_COLOR,
|
||||
@ -7,140 +11,545 @@ import {
|
||||
UP_SCALE,
|
||||
} from "./Constants";
|
||||
import { useTransform } from "./TransformContext";
|
||||
import { useCallback, useState } from "react";
|
||||
import { StationData } from "./types";
|
||||
import { useMapData } from "./MapDataContext";
|
||||
import { coordinatesToLocal } from "./utils";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { languageStore } from "@shared";
|
||||
// --- Конец заглушек ---
|
||||
|
||||
// --- Декларации для react-pixi ---
|
||||
// (В реальном проекте типы должны быть предоставлены библиотекой @pixi/react-pixi)
|
||||
declare const pixiContainer: any;
|
||||
declare const pixiGraphics: any;
|
||||
declare const pixiText: any;
|
||||
|
||||
// --- Типы ---
|
||||
type HorizontalAlign = "left" | "center" | "right";
|
||||
type VerticalAlign = "top" | "center" | "bottom";
|
||||
type TextAlign = HorizontalAlign | `${HorizontalAlign} ${VerticalAlign}`;
|
||||
type LabelAlign = "left" | "center" | "right";
|
||||
|
||||
// --- Утилиты ---
|
||||
|
||||
/**
|
||||
* Преобразует текстовое позиционирование в anchor координаты.
|
||||
*/
|
||||
const getAnchorFromTextAlign = (align: TextAlign): { x: number; y: number } => {
|
||||
const parts = align.split(" ");
|
||||
const horizontal = parts[0] as HorizontalAlign;
|
||||
const vertical = (parts[1] as VerticalAlign) || "center";
|
||||
const horizontalMap: Record<HorizontalAlign, number> = {
|
||||
left: 0,
|
||||
center: 0.5,
|
||||
right: 1,
|
||||
};
|
||||
const verticalMap: Record<VerticalAlign, number> = {
|
||||
top: 0,
|
||||
center: 0.5,
|
||||
bottom: 1,
|
||||
};
|
||||
return { x: horizontalMap[horizontal], y: verticalMap[vertical] };
|
||||
};
|
||||
|
||||
/**
|
||||
* Получает координату anchor.x из типа выравнивания.
|
||||
*/
|
||||
|
||||
// --- Интерфейсы пропсов ---
|
||||
|
||||
interface StationProps {
|
||||
station: StationData;
|
||||
ruLabel: string | null;
|
||||
anchorPoint?: { x: number; y: number };
|
||||
/** Anchor для всего блока с текстом. По умолчанию: `"right center"` */
|
||||
labelBlockAnchor?: TextAlign | { x: number; y: number };
|
||||
/** Внутреннее выравнивание текста в блоке. По умолчанию: `"left"` */
|
||||
labelAlign?: LabelAlign;
|
||||
/** Callback для изменения внутреннего выравнивания */
|
||||
onLabelAlignChange?: (align: LabelAlign) => void;
|
||||
}
|
||||
|
||||
export const Station = observer(
|
||||
({ station, ruLabel }: Readonly<StationProps>) => {
|
||||
const draw = useCallback((g: Graphics) => {
|
||||
interface LabelAlignmentControlProps {
|
||||
scale: number;
|
||||
currentAlign: LabelAlign;
|
||||
onAlignChange: (align: LabelAlign) => void;
|
||||
onPointerOver: () => void;
|
||||
onPointerOut: () => void;
|
||||
onControlPointerEnter: () => void;
|
||||
onControlPointerLeave: () => void;
|
||||
}
|
||||
|
||||
interface StationLabelProps
|
||||
extends Omit<StationProps, "ruLabelAnchor" | "nameLabelAnchor"> {}
|
||||
|
||||
// =========================================================================
|
||||
// Компонент: Панель управления выравниванием в стиле УрФУ
|
||||
// =========================================================================
|
||||
|
||||
const LabelAlignmentControl: FC<LabelAlignmentControlProps> = ({
|
||||
scale,
|
||||
currentAlign,
|
||||
onAlignChange,
|
||||
|
||||
onControlPointerEnter,
|
||||
onControlPointerLeave,
|
||||
}) => {
|
||||
const controlHeight = 50 / scale;
|
||||
const controlWidth = 200 / scale;
|
||||
const fontSize = 18 / scale;
|
||||
const borderRadius = 8 / scale;
|
||||
const compensatedRuFontSize = (26 * 0.75) / scale;
|
||||
const buttonWidth = controlWidth / 3;
|
||||
const strokeWidth = 2 / scale;
|
||||
|
||||
const drawBg = useCallback(
|
||||
(g: Graphics) => {
|
||||
g.clear();
|
||||
|
||||
// Основной фон с градиентом
|
||||
g.roundRect(
|
||||
-controlWidth / 2,
|
||||
0,
|
||||
controlWidth,
|
||||
controlHeight,
|
||||
borderRadius
|
||||
);
|
||||
g.fill({ color: "#1a1a1a" }); // Темный фон как у УрФУ
|
||||
|
||||
// Тонкая рамка
|
||||
g.roundRect(
|
||||
-controlWidth / 2,
|
||||
0,
|
||||
controlWidth,
|
||||
controlHeight,
|
||||
borderRadius
|
||||
);
|
||||
g.stroke({ color: "#333333", width: strokeWidth });
|
||||
|
||||
// Разделители между кнопками
|
||||
for (let i = 1; i < 3; i++) {
|
||||
const x = -controlWidth / 2 + buttonWidth * i;
|
||||
g.moveTo(x, strokeWidth);
|
||||
g.lineTo(x, controlHeight - strokeWidth);
|
||||
g.stroke({ color: "#333333", width: strokeWidth });
|
||||
}
|
||||
},
|
||||
[controlWidth, controlHeight, borderRadius, buttonWidth, strokeWidth]
|
||||
);
|
||||
|
||||
const drawButtonHighlight = useCallback(
|
||||
(g: Graphics, index: number, isActive: boolean) => {
|
||||
g.clear();
|
||||
|
||||
if (isActive) {
|
||||
const x = -controlWidth / 2 + buttonWidth * index;
|
||||
g.roundRect(
|
||||
x + strokeWidth,
|
||||
strokeWidth,
|
||||
buttonWidth - strokeWidth * 2,
|
||||
controlHeight - strokeWidth * 2,
|
||||
borderRadius / 2
|
||||
);
|
||||
g.fill({ color: "#0066cc", alpha: 0.8 }); // Синий акцент УрФУ
|
||||
}
|
||||
},
|
||||
[controlWidth, controlHeight, buttonWidth, strokeWidth, borderRadius]
|
||||
);
|
||||
|
||||
const getTextStyle = (isActive: boolean) => ({
|
||||
fontSize,
|
||||
fontWeight: isActive ? ("bold" as const) : ("normal" as const),
|
||||
fill: isActive ? "#ffffff" : "#cccccc",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
});
|
||||
|
||||
const alignOptions = [
|
||||
{ key: "left" as const, label: "Left" },
|
||||
{ key: "center" as const, label: "Center" },
|
||||
{ key: "right" as const, label: "Right" },
|
||||
];
|
||||
|
||||
return (
|
||||
<pixiContainer
|
||||
position={{ x: 0, y: compensatedRuFontSize * 1.1 + 15 / scale }}
|
||||
eventMode="static"
|
||||
onPointerOver={(e: FederatedMouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onControlPointerEnter();
|
||||
}}
|
||||
onPointerOut={(e: FederatedMouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onControlPointerLeave();
|
||||
}}
|
||||
onPointerDown={(e: FederatedMouseEvent) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{/* Основной фон */}
|
||||
<pixiGraphics draw={drawBg} />
|
||||
|
||||
{/* Кнопки с подсветкой */}
|
||||
{alignOptions.map((option, index) => (
|
||||
<pixiContainer key={option.key}>
|
||||
{/* Подсветка активной кнопки */}
|
||||
<pixiGraphics
|
||||
draw={(g: Graphics) =>
|
||||
drawButtonHighlight(g, index, option.key === currentAlign)
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Текст кнопки */}
|
||||
<pixiText
|
||||
text={option.label}
|
||||
anchor={{ x: 0.5, y: 0.5 }}
|
||||
position={{
|
||||
x: -controlWidth / 2 + buttonWidth * (index + 0.5),
|
||||
y: controlHeight / 2,
|
||||
}}
|
||||
style={getTextStyle(option.key === currentAlign)}
|
||||
eventMode="static"
|
||||
cursor="pointer"
|
||||
onClick={(e: FederatedMouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onAlignChange(option.key);
|
||||
}}
|
||||
onPointerDown={(e: FederatedMouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onAlignChange(option.key);
|
||||
}}
|
||||
onPointerOver={(e: FederatedMouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onControlPointerEnter();
|
||||
}}
|
||||
/>
|
||||
</pixiContainer>
|
||||
))}
|
||||
</pixiContainer>
|
||||
);
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Компонент: Метка Станции (с логикой)
|
||||
// =========================================================================
|
||||
|
||||
const StationLabel = observer(
|
||||
({
|
||||
station,
|
||||
ruLabel,
|
||||
anchorPoint,
|
||||
labelBlockAnchor: labelBlockAnchorProp,
|
||||
labelAlign: labelAlignProp = "center",
|
||||
onLabelAlignChange,
|
||||
}: Readonly<StationLabelProps>) => {
|
||||
const { language } = languageStore;
|
||||
const { rotation, scale } = useTransform();
|
||||
const { setStationOffset, setStationAlign } = useMapData();
|
||||
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isPointerDown, setIsPointerDown] = useState(false);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [isControlHovered, setIsControlHovered] = useState(false);
|
||||
const [currentLabelAlign, setCurrentLabelAlign] = useState(labelAlignProp);
|
||||
const [ruLabelWidth, setRuLabelWidth] = useState(0);
|
||||
|
||||
const dragStartPos = useRef({ x: 0, y: 0 });
|
||||
const mouseStartPos = useRef({ x: 0, y: 0 });
|
||||
const hideTimer = useRef<NodeJS.Timeout | null>(null);
|
||||
const ruLabelRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (hideTimer.current) {
|
||||
clearTimeout(hideTimer.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handlePointerEnter = () => {
|
||||
if (hideTimer.current) {
|
||||
clearTimeout(hideTimer.current);
|
||||
hideTimer.current = null;
|
||||
}
|
||||
setIsHovered(true);
|
||||
};
|
||||
|
||||
const handleControlPointerEnter = () => {
|
||||
// Дополнительная обработка для панели управления
|
||||
if (hideTimer.current) {
|
||||
clearTimeout(hideTimer.current);
|
||||
hideTimer.current = null;
|
||||
}
|
||||
setIsControlHovered(true);
|
||||
setIsHovered(true);
|
||||
};
|
||||
|
||||
const handleControlPointerLeave = () => {
|
||||
setIsControlHovered(false);
|
||||
// Если курсор не над основным контейнером, скрываем панель через некоторое время
|
||||
if (!isHovered) {
|
||||
hideTimer.current = setTimeout(() => {
|
||||
setIsHovered(false);
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerLeave = () => {
|
||||
// Увеличиваем время до скрытия панели и добавляем проверку
|
||||
hideTimer.current = setTimeout(() => {
|
||||
setIsHovered(false);
|
||||
// Если курсор не над панелью управления, скрываем и её
|
||||
if (!isControlHovered) {
|
||||
setIsControlHovered(false);
|
||||
}
|
||||
}, 100); // Увеличиваем время до скрытия панели
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPosition({ x: station.offset_x ?? 0, y: station.offset_y ?? 0 });
|
||||
}, [station.offset_x, station.offset_y, station.id]);
|
||||
|
||||
// Функция для конвертации числового align в строковый
|
||||
const convertNumericAlign = (align: number): LabelAlign => {
|
||||
switch (align) {
|
||||
case 0:
|
||||
return "left";
|
||||
case 1:
|
||||
return "center";
|
||||
case 2:
|
||||
return "right";
|
||||
default:
|
||||
return "center";
|
||||
}
|
||||
};
|
||||
|
||||
// Функция для конвертации строкового align в числовой
|
||||
const convertStringAlign = (align: LabelAlign): number => {
|
||||
switch (align) {
|
||||
case "left":
|
||||
return 0;
|
||||
case "center":
|
||||
return 1;
|
||||
case "right":
|
||||
return 2;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentLabelAlign(convertNumericAlign(station.align ?? 1));
|
||||
}, [station.align]);
|
||||
|
||||
if (!station) return null;
|
||||
|
||||
const coordinates = coordinatesToLocal(station.latitude, station.longitude);
|
||||
const compensatedRuFontSize = (26 * 0.75) / scale;
|
||||
const compensatedNameFontSize = (16 * 0.75) / scale;
|
||||
const minDistance = 30;
|
||||
const compensatedOffset = Math.max(minDistance / scale, 24 / scale);
|
||||
const textBlockPosition = anchorPoint || position;
|
||||
const finalLabelBlockAnchor = labelBlockAnchorProp || "right center";
|
||||
const labelBlockAnchor =
|
||||
typeof finalLabelBlockAnchor === "string"
|
||||
? getAnchorFromTextAlign(finalLabelBlockAnchor)
|
||||
: finalLabelBlockAnchor;
|
||||
|
||||
// Измеряем ширину верхнего лейбла
|
||||
useEffect(() => {
|
||||
if (ruLabelRef.current && ruLabel) {
|
||||
setRuLabelWidth(ruLabelRef.current.width);
|
||||
}
|
||||
}, [ruLabel, compensatedRuFontSize]);
|
||||
|
||||
const handlePointerDown = (e: FederatedMouseEvent) => {
|
||||
setIsPointerDown(true);
|
||||
setIsDragging(false);
|
||||
dragStartPos.current = { ...position };
|
||||
mouseStartPos.current = { x: e.global.x, y: e.global.y };
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const handlePointerMove = (e: FederatedMouseEvent) => {
|
||||
if (!isPointerDown) return;
|
||||
if (!isDragging) {
|
||||
const dx = e.global.x - mouseStartPos.current.x;
|
||||
const dy = e.global.y - mouseStartPos.current.y;
|
||||
if (Math.hypot(dx, dy) > 3) setIsDragging(true);
|
||||
else return;
|
||||
}
|
||||
const dx = (e.global.x - mouseStartPos.current.x) / scale;
|
||||
const dy = (e.global.y - mouseStartPos.current.y) / scale;
|
||||
const newPosition = {
|
||||
x: dragStartPos.current.x + dx,
|
||||
y: dragStartPos.current.y + dy,
|
||||
};
|
||||
|
||||
// Проверяем, изменилась ли позиция
|
||||
if (
|
||||
Math.abs(newPosition.x - position.x) > 0.01 ||
|
||||
Math.abs(newPosition.y - position.y) > 0.01
|
||||
) {
|
||||
setPosition(newPosition);
|
||||
setStationOffset(station.id, newPosition.x, newPosition.y);
|
||||
}
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const handlePointerUp = (e: FederatedMouseEvent) => {
|
||||
setIsPointerDown(false);
|
||||
setTimeout(() => setIsDragging(false), 50);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const handleAlignChange = async (align: LabelAlign) => {
|
||||
setCurrentLabelAlign(align);
|
||||
onLabelAlignChange?.(align);
|
||||
// Сохраняем в стор
|
||||
const numericAlign = convertStringAlign(align);
|
||||
setStationAlign(station.id, numericAlign);
|
||||
};
|
||||
|
||||
// Функция для расчета позиции нижнего лейбла относительно ширины верхнего
|
||||
const getSecondLabelPosition = (): number => {
|
||||
if (!ruLabelWidth) return 0;
|
||||
|
||||
switch (currentLabelAlign) {
|
||||
case "left":
|
||||
// Позиционируем относительно левого края верхнего текста
|
||||
return -ruLabelWidth / 2;
|
||||
case "center":
|
||||
// Центрируем относительно центра верхнего текста
|
||||
return 0;
|
||||
case "right":
|
||||
// Позиционируем относительно правого края верхнего текста
|
||||
return ruLabelWidth / 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Функция для расчета anchor нижнего лейбла
|
||||
const getSecondLabelAnchor = (): number => {
|
||||
switch (currentLabelAlign) {
|
||||
case "left":
|
||||
return 0; // anchor.x = 0 (левый край)
|
||||
case "center":
|
||||
return 0.5; // anchor.x = 0.5 (центр)
|
||||
case "right":
|
||||
return 1; // anchor.x = 1 (правый край)
|
||||
default:
|
||||
return 0.5;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<pixiContainer
|
||||
x={coordinates.x * UP_SCALE}
|
||||
y={coordinates.y * UP_SCALE}
|
||||
rotation={-rotation}
|
||||
eventMode="static"
|
||||
interactive
|
||||
cursor={isDragging ? "grabbing" : "grab"}
|
||||
onPointerOver={handlePointerEnter}
|
||||
onPointerOut={handlePointerLeave}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerUpOutside={handlePointerUp}
|
||||
onGlobalPointerMove={handlePointerMove}
|
||||
>
|
||||
<pixiContainer
|
||||
position={{
|
||||
x:
|
||||
textBlockPosition.x +
|
||||
compensatedOffset * (labelBlockAnchor.x === 1 ? -1 : 1),
|
||||
y: textBlockPosition.y,
|
||||
}}
|
||||
anchor={labelBlockAnchor}
|
||||
>
|
||||
{ruLabel && (
|
||||
<pixiText
|
||||
ref={ruLabelRef}
|
||||
text={ruLabel}
|
||||
position={{ x: 0, y: 0 }}
|
||||
anchor={{ x: 0.5, y: 0.5 }}
|
||||
style={{
|
||||
fontSize: compensatedRuFontSize,
|
||||
fontWeight: "bold",
|
||||
fill: "#ffffff",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{station.name && language !== "ru" && ruLabel && (
|
||||
<pixiText
|
||||
text={station.name}
|
||||
position={{
|
||||
x: getSecondLabelPosition(),
|
||||
y: compensatedRuFontSize * 1.1,
|
||||
}}
|
||||
anchor={{ x: getSecondLabelAnchor(), y: 0.5 }}
|
||||
style={{
|
||||
fontSize: compensatedNameFontSize,
|
||||
fontWeight: "bold",
|
||||
fill: "#CCCCCC",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(isHovered || isControlHovered) && !isDragging && (
|
||||
<LabelAlignmentControl
|
||||
scale={scale}
|
||||
currentAlign={currentLabelAlign}
|
||||
onAlignChange={handleAlignChange}
|
||||
onPointerOver={handlePointerEnter}
|
||||
onPointerOut={handlePointerLeave}
|
||||
onControlPointerEnter={handleControlPointerEnter}
|
||||
onControlPointerLeave={handleControlPointerLeave}
|
||||
/>
|
||||
)}
|
||||
</pixiContainer>
|
||||
</pixiContainer>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// =========================================================================
|
||||
// Главный экспортируемый компонент: Станция
|
||||
// =========================================================================
|
||||
|
||||
export const Station = ({
|
||||
station,
|
||||
ruLabel,
|
||||
anchorPoint,
|
||||
labelBlockAnchor,
|
||||
labelAlign,
|
||||
onLabelAlignChange,
|
||||
}: Readonly<StationProps>) => {
|
||||
const draw = useCallback(
|
||||
(g: Graphics) => {
|
||||
g.clear();
|
||||
const coordinates = coordinatesToLocal(
|
||||
station.latitude,
|
||||
station.longitude
|
||||
);
|
||||
g.circle(
|
||||
coordinates.x * UP_SCALE,
|
||||
coordinates.y * UP_SCALE,
|
||||
STATION_RADIUS
|
||||
);
|
||||
const radius = STATION_RADIUS;
|
||||
g.circle(coordinates.x * UP_SCALE, coordinates.y * UP_SCALE, radius);
|
||||
g.fill({ color: PATH_COLOR });
|
||||
g.stroke({ color: BACKGROUND_COLOR, width: STATION_OUTLINE_WIDTH });
|
||||
}, []);
|
||||
},
|
||||
[station.latitude, station.longitude]
|
||||
);
|
||||
|
||||
return (
|
||||
<pixiContainer>
|
||||
<pixiGraphics draw={draw} />
|
||||
<StationLabel station={station} ruLabel={ruLabel} />
|
||||
</pixiContainer>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const StationLabel = observer(
|
||||
({ station, ruLabel }: Readonly<StationProps>) => {
|
||||
const { rotation, scale } = useTransform();
|
||||
const { setStationOffset } = useMapData();
|
||||
|
||||
const [position, setPosition] = useState({
|
||||
x: station.offset_x,
|
||||
y: station.offset_y,
|
||||
});
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [startPosition, setStartPosition] = useState({ x: 0, y: 0 });
|
||||
const [startMousePosition, setStartMousePosition] = useState({
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
if (!station) {
|
||||
console.error("station is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
const handlePointerDown = (e: FederatedMouseEvent) => {
|
||||
setIsDragging(true);
|
||||
setStartPosition({
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
});
|
||||
setStartMousePosition({
|
||||
x: e.globalX,
|
||||
y: e.globalY,
|
||||
});
|
||||
|
||||
e.stopPropagation();
|
||||
};
|
||||
const handlePointerMove = (e: FederatedMouseEvent) => {
|
||||
if (!isDragging) return;
|
||||
const dx = e.globalX - startMousePosition.x;
|
||||
const dy = e.globalY - startMousePosition.y;
|
||||
const newPosition = {
|
||||
x: startPosition.x + dx,
|
||||
y: startPosition.y + dy,
|
||||
};
|
||||
setPosition(newPosition);
|
||||
setStationOffset(station.id, newPosition.x, newPosition.y);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const handlePointerUp = (e: FederatedMouseEvent) => {
|
||||
setIsDragging(false);
|
||||
e.stopPropagation();
|
||||
};
|
||||
const coordinates = coordinatesToLocal(station.latitude, station.longitude);
|
||||
|
||||
return (
|
||||
<pixiContainer
|
||||
eventMode="static"
|
||||
interactive
|
||||
onPointerDown={handlePointerDown}
|
||||
onGlobalPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerUpOutside={handlePointerUp}
|
||||
width={48}
|
||||
height={48}
|
||||
x={coordinates.x * UP_SCALE}
|
||||
y={coordinates.y * UP_SCALE}
|
||||
rotation={-rotation}
|
||||
>
|
||||
<pixiText
|
||||
anchor={{ x: 1, y: 0.5 }}
|
||||
text={station.name}
|
||||
position={{
|
||||
x: position.x / scale + 24,
|
||||
y: position.y / scale,
|
||||
}}
|
||||
style={{
|
||||
fontSize: 26,
|
||||
fontWeight: "bold",
|
||||
fill: "#ffffff",
|
||||
}}
|
||||
/>
|
||||
|
||||
{ruLabel && (
|
||||
<pixiText
|
||||
anchor={{ x: 1, y: -1 }}
|
||||
text={ruLabel}
|
||||
position={{
|
||||
x: position.x / scale + 24,
|
||||
y: position.y / scale,
|
||||
}}
|
||||
style={{
|
||||
fontSize: 16,
|
||||
fontWeight: "bold",
|
||||
fill: "#CCCCCC",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</pixiContainer>
|
||||
);
|
||||
}
|
||||
);
|
||||
return (
|
||||
<pixiContainer>
|
||||
<pixiGraphics draw={draw} />
|
||||
<StationLabel
|
||||
station={station}
|
||||
ruLabel={ruLabel}
|
||||
anchorPoint={anchorPoint}
|
||||
labelBlockAnchor={labelBlockAnchor}
|
||||
labelAlign={labelAlign}
|
||||
onLabelAlignChange={onLabelAlignChange}
|
||||
/>
|
||||
</pixiContainer>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user