Updated transformer to also transform attached tokens
This commit is contained in:
parent
54debeb61c
commit
3ed6e63080
@ -287,7 +287,7 @@ function Note({
|
|||||||
</animated.Group>
|
</animated.Group>
|
||||||
<Transformer
|
<Transformer
|
||||||
active={(!note.locked && selected) || isTransforming}
|
active={(!note.locked && selected) || isTransforming}
|
||||||
nodeRef={noteRef}
|
nodes={() => (noteRef.current ? [noteRef.current] : [])}
|
||||||
onTransformEnd={handleTransformEnd}
|
onTransformEnd={handleTransformEnd}
|
||||||
onTransformStart={handleTransformStart}
|
onTransformStart={handleTransformStart}
|
||||||
gridScale={map?.grid.measurement.scale || ""}
|
gridScale={map?.grid.measurement.scale || ""}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState, useRef } from "react";
|
import { useState, useRef, useCallback, useMemo } from "react";
|
||||||
import { Image as KonvaImage, Group } from "react-konva";
|
import { Image as KonvaImage, Group } from "react-konva";
|
||||||
import { useSpring, animated } from "@react-spring/konva";
|
import { useSpring, animated } from "@react-spring/konva";
|
||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
@ -31,9 +31,11 @@ import {
|
|||||||
TokenMenuCloseChangeEventHandler,
|
TokenMenuCloseChangeEventHandler,
|
||||||
TokenMenuOpenChangeEventHandler,
|
TokenMenuOpenChangeEventHandler,
|
||||||
TokenStateChangeEventHandler,
|
TokenStateChangeEventHandler,
|
||||||
|
TokenTransformEventHandler,
|
||||||
} from "../../types/Events";
|
} from "../../types/Events";
|
||||||
import Transformer from "./Transformer";
|
import Transformer from "./Transformer";
|
||||||
import TokenAttachment from "./TokenAttachment";
|
import TokenAttachment from "./TokenAttachment";
|
||||||
|
import { MapState } from "../../types/MapState";
|
||||||
|
|
||||||
type MapTokenProps = {
|
type MapTokenProps = {
|
||||||
tokenState: TokenState;
|
tokenState: TokenState;
|
||||||
@ -42,10 +44,14 @@ type MapTokenProps = {
|
|||||||
onTokenMenuClose: TokenMenuCloseChangeEventHandler;
|
onTokenMenuClose: TokenMenuCloseChangeEventHandler;
|
||||||
onTokenDragStart: TokenDragEventHandler;
|
onTokenDragStart: TokenDragEventHandler;
|
||||||
onTokenDragEnd: TokenDragEventHandler;
|
onTokenDragEnd: TokenDragEventHandler;
|
||||||
|
onTokenTransformStart: TokenTransformEventHandler;
|
||||||
|
onTokenTransformEnd: TokenTransformEventHandler;
|
||||||
|
transforming: boolean;
|
||||||
draggable: boolean;
|
draggable: boolean;
|
||||||
selectable: boolean;
|
selectable: boolean;
|
||||||
fadeOnHover: boolean;
|
fadeOnHover: boolean;
|
||||||
map: Map;
|
map: Map;
|
||||||
|
mapState: MapState;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,10 +62,14 @@ function Token({
|
|||||||
onTokenMenuClose,
|
onTokenMenuClose,
|
||||||
onTokenDragStart,
|
onTokenDragStart,
|
||||||
onTokenDragEnd,
|
onTokenDragEnd,
|
||||||
|
onTokenTransformStart,
|
||||||
|
onTokenTransformEnd,
|
||||||
|
transforming,
|
||||||
draggable,
|
draggable,
|
||||||
selectable,
|
selectable,
|
||||||
fadeOnHover,
|
fadeOnHover,
|
||||||
map,
|
map,
|
||||||
|
mapState,
|
||||||
selected,
|
selected,
|
||||||
}: MapTokenProps) {
|
}: MapTokenProps) {
|
||||||
const userId = useUserId();
|
const userId = useUserId();
|
||||||
@ -86,64 +96,24 @@ function Token({
|
|||||||
const [attachmentOverCharacter, setAttachmentOverCharacter] = useState(false);
|
const [attachmentOverCharacter, setAttachmentOverCharacter] = useState(false);
|
||||||
// The characters that we're present when an attachment is dragged, used to highlight the attachment
|
// The characters that we're present when an attachment is dragged, used to highlight the attachment
|
||||||
const attachmentCharactersRef = useRef<Konva.Node[]>([]);
|
const attachmentCharactersRef = useRef<Konva.Node[]>([]);
|
||||||
const attachmentThreshold = Vector2.componentMin(gridCellPixelSize) / 4;
|
const attachmentThreshold = useMemo(
|
||||||
|
() => Vector2.componentMin(gridCellPixelSize) / 4,
|
||||||
|
[gridCellPixelSize]
|
||||||
|
);
|
||||||
|
|
||||||
function handleDragStart(event: Konva.KonvaEventObject<DragEvent>) {
|
function handleDragStart(event: Konva.KonvaEventObject<DragEvent>) {
|
||||||
const tokenGroup = event.target as Konva.Shape;
|
const tokenGroup = event.target as Konva.Shape;
|
||||||
const layer = tokenGroup.getLayer();
|
|
||||||
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
previousDragPositionRef.current = tokenGroup.position();
|
previousDragPositionRef.current = tokenGroup.position();
|
||||||
|
|
||||||
if (tokenState.category === "vehicle") {
|
attachedTokensRef.current = getAttachedTokens();
|
||||||
const tokenIntersection = new Intersection(
|
|
||||||
getScaledOutline(tokenState, tokenWidth, tokenHeight),
|
|
||||||
{ x: tokenX - tokenWidth / 2, y: tokenY - tokenHeight / 2 },
|
|
||||||
{ x: tokenX, y: tokenY },
|
|
||||||
tokenState.rotation
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find all other characters on the map and check whether they're
|
|
||||||
// intersecting the vehicle
|
|
||||||
const characters = layer.find(".character");
|
|
||||||
const attachments = layer.find(".attachment");
|
|
||||||
const tokens = [...characters, ...attachments];
|
|
||||||
for (let other of tokens) {
|
|
||||||
if (other === tokenGroup) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (tokenIntersection.intersects(other.position())) {
|
|
||||||
attachedTokensRef.current.push(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tokenState.category === "attachment") {
|
if (tokenState.category === "attachment") {
|
||||||
// If we're dragging an attachment add all characters to the attachment characters
|
// If we're dragging an attachment add all characters to the attachment characters
|
||||||
// So we can check for highlights
|
// So we can check for highlights
|
||||||
previousDragPositionRef.current = tokenGroup.position();
|
const characters = tokenGroup.getLayer()?.find(".character") || [];
|
||||||
const characters = layer.find(".character");
|
|
||||||
attachmentCharactersRef.current = characters;
|
attachmentCharactersRef.current = characters;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenState.category === "character") {
|
|
||||||
// Find all attachments and check whether they are close to the center of this token
|
|
||||||
const attachments = layer.find(".attachment");
|
|
||||||
for (let attachment of attachments) {
|
|
||||||
if (attachment === tokenGroup) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const distance = Vector2.distance(
|
|
||||||
tokenGroup.position(),
|
|
||||||
attachment.position()
|
|
||||||
);
|
|
||||||
if (distance < attachmentThreshold) {
|
|
||||||
attachedTokensRef.current.push(attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setDragging(true);
|
setDragging(true);
|
||||||
onTokenDragStart(
|
onTokenDragStart(
|
||||||
event,
|
event,
|
||||||
@ -228,8 +198,8 @@ function Token({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
if (selectable && draggable && tokenRef.current) {
|
if (selectable && draggable && transformRootRef.current) {
|
||||||
onTokenMenuOpen(tokenState.id, tokenRef.current, true);
|
onTokenMenuOpen(tokenState.id, transformRootRef.current, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,11 +221,11 @@ function Token({
|
|||||||
}
|
}
|
||||||
// Check token click when locked and selectable
|
// Check token click when locked and selectable
|
||||||
// We can't use onClick because that doesn't check pointer distance
|
// We can't use onClick because that doesn't check pointer distance
|
||||||
if (tokenState.locked && selectable && tokenRef.current) {
|
if (tokenState.locked && selectable && transformRootRef.current) {
|
||||||
// If down and up time is small trigger a click
|
// If down and up time is small trigger a click
|
||||||
const delta = event.evt.timeStamp - tokenPointerDownTimeRef.current;
|
const delta = event.evt.timeStamp - tokenPointerDownTimeRef.current;
|
||||||
if (delta < 300) {
|
if (delta < 300) {
|
||||||
onTokenMenuOpen(tokenState.id, tokenRef.current, true);
|
onTokenMenuOpen(tokenState.id, transformRootRef.current, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,30 +242,7 @@ function Token({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenRef = useRef<Konva.Group>(null);
|
const transformRootRef = useRef<Konva.Group>(null);
|
||||||
|
|
||||||
const [isTransforming, setIsTransforming] = useState(false);
|
|
||||||
function handleTransformStart() {
|
|
||||||
setIsTransforming(true);
|
|
||||||
onTokenMenuClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTransformEnd(event: Konva.KonvaEventObject<Event>) {
|
|
||||||
if (tokenRef.current) {
|
|
||||||
const sizeChange = event.target.scaleX();
|
|
||||||
const rotation = event.target.rotation();
|
|
||||||
onTokenStateChange({
|
|
||||||
[tokenState.id]: {
|
|
||||||
size: tokenState.size * sizeChange,
|
|
||||||
rotation: rotation,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
tokenRef.current.scaleX(1);
|
|
||||||
tokenRef.current.scaleY(1);
|
|
||||||
onTokenMenuOpen(tokenState.id, tokenRef.current, false);
|
|
||||||
}
|
|
||||||
setIsTransforming(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const minCellSize = Math.min(
|
const minCellSize = Math.min(
|
||||||
gridCellPixelSize.width,
|
gridCellPixelSize.width,
|
||||||
@ -317,6 +264,132 @@ function Token({
|
|||||||
immediate: skipAnimation,
|
immediate: skipAnimation,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getAttachedTokens = useCallback(() => {
|
||||||
|
const transformRoot = transformRootRef.current;
|
||||||
|
const tokenGroup = transformRoot?.parent;
|
||||||
|
const layer = transformRoot?.getLayer();
|
||||||
|
let attachedTokens: Konva.Node[] = [];
|
||||||
|
if (tokenGroup && layer) {
|
||||||
|
if (tokenState.category === "vehicle") {
|
||||||
|
const tokenIntersection = new Intersection(
|
||||||
|
getScaledOutline(tokenState, tokenWidth, tokenHeight),
|
||||||
|
{ x: tokenX - tokenWidth / 2, y: tokenY - tokenHeight / 2 },
|
||||||
|
{ x: tokenX, y: tokenY },
|
||||||
|
tokenState.rotation
|
||||||
|
);
|
||||||
|
|
||||||
|
// Find all other characters on the map and check whether they're
|
||||||
|
// intersecting the vehicle
|
||||||
|
const characters = layer.find(".character");
|
||||||
|
const attachments = layer.find(".attachment");
|
||||||
|
const tokens = [...characters, ...attachments];
|
||||||
|
for (let other of tokens) {
|
||||||
|
const id = other.id();
|
||||||
|
if (id in mapState.tokens) {
|
||||||
|
const position = {
|
||||||
|
x: mapState.tokens[id].x * mapWidth,
|
||||||
|
y: mapState.tokens[id].y * mapHeight,
|
||||||
|
};
|
||||||
|
if (tokenIntersection.intersects(position)) {
|
||||||
|
attachedTokens.push(other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenState.category === "character") {
|
||||||
|
// Find all attachments and check whether they are close to the center of this token
|
||||||
|
const attachments = layer.find(".attachment");
|
||||||
|
for (let attachment of attachments) {
|
||||||
|
const id = attachment.id();
|
||||||
|
if (id in mapState.tokens) {
|
||||||
|
const position = {
|
||||||
|
x: mapState.tokens[id].x * mapWidth,
|
||||||
|
y: mapState.tokens[id].y * mapHeight,
|
||||||
|
};
|
||||||
|
const distance = Vector2.distance(tokenGroup.position(), position);
|
||||||
|
if (distance < attachmentThreshold) {
|
||||||
|
attachedTokens.push(attachment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attachedTokens;
|
||||||
|
}, [
|
||||||
|
attachmentThreshold,
|
||||||
|
tokenHeight,
|
||||||
|
tokenWidth,
|
||||||
|
tokenState,
|
||||||
|
tokenX,
|
||||||
|
tokenY,
|
||||||
|
mapState,
|
||||||
|
mapWidth,
|
||||||
|
mapHeight,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Override transform active to always show this transformer when using it
|
||||||
|
const [overrideTransformActive, setOverrideTransformActive] = useState(false);
|
||||||
|
|
||||||
|
function handleTransformStart(event: Konva.KonvaEventObject<Event>) {
|
||||||
|
setOverrideTransformActive(true);
|
||||||
|
onTokenTransformStart(event);
|
||||||
|
onTokenMenuClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTransformEnd(event: Konva.KonvaEventObject<Event>) {
|
||||||
|
const transformer = event.currentTarget as Konva.Transformer;
|
||||||
|
const nodes = transformer.nodes();
|
||||||
|
const tokenChanges: Record<string, Partial<TokenState>> = {};
|
||||||
|
for (let node of nodes) {
|
||||||
|
const id = node.id();
|
||||||
|
if (id in mapState.tokens) {
|
||||||
|
const sizeChange = node.scaleX();
|
||||||
|
const rotation = node.rotation();
|
||||||
|
const xChange = node.x() / mapWidth;
|
||||||
|
const yChange = node.y() / mapHeight;
|
||||||
|
tokenChanges[id] = {
|
||||||
|
size: mapState.tokens[id].size * sizeChange,
|
||||||
|
rotation: rotation,
|
||||||
|
x: mapState.tokens[id].x + xChange,
|
||||||
|
y: mapState.tokens[id].y + yChange,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
node.scaleX(1);
|
||||||
|
node.scaleY(1);
|
||||||
|
node.x(0);
|
||||||
|
node.y(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTokenStateChange(tokenChanges);
|
||||||
|
if (transformRootRef.current) {
|
||||||
|
onTokenMenuOpen(tokenState.id, transformRootRef.current, false);
|
||||||
|
}
|
||||||
|
setOverrideTransformActive(false);
|
||||||
|
onTokenTransformEnd(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
const transformerActive = useMemo(
|
||||||
|
() => (!tokenState.locked && selected) || overrideTransformActive,
|
||||||
|
[tokenState, selected, overrideTransformActive]
|
||||||
|
);
|
||||||
|
|
||||||
|
const transformerNodes = useMemo(
|
||||||
|
() => () => {
|
||||||
|
if (transformRootRef.current) {
|
||||||
|
// Find attached transform roots
|
||||||
|
const attached = getAttachedTokens().map((node) =>
|
||||||
|
(node as Konva.Group).findOne(".transform-root")
|
||||||
|
);
|
||||||
|
return [transformRootRef.current, ...attached];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[getAttachedTokens]
|
||||||
|
);
|
||||||
|
|
||||||
// When a token is hidden if you aren't the map owner hide it completely
|
// When a token is hidden if you aren't the map owner hide it completely
|
||||||
if (map && !tokenState.visible && map.owner !== userId) {
|
if (map && !tokenState.visible && map.owner !== userId) {
|
||||||
return null;
|
return null;
|
||||||
@ -354,7 +427,9 @@ function Token({
|
|||||||
id={tokenState.id}
|
id={tokenState.id}
|
||||||
>
|
>
|
||||||
<Group
|
<Group
|
||||||
ref={tokenRef}
|
ref={transformRootRef}
|
||||||
|
id={tokenState.id}
|
||||||
|
name="transform-root"
|
||||||
rotation={tokenState.rotation}
|
rotation={tokenState.rotation}
|
||||||
offsetX={tokenWidth / 2}
|
offsetX={tokenWidth / 2}
|
||||||
offsetY={tokenHeight / 2}
|
offsetY={tokenHeight / 2}
|
||||||
@ -378,7 +453,7 @@ function Token({
|
|||||||
hitFunc={() => {}}
|
hitFunc={() => {}}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
{!isTransforming ? (
|
{!transforming ? (
|
||||||
<Group offsetX={tokenWidth / 2} offsetY={tokenHeight / 2}>
|
<Group offsetX={tokenWidth / 2} offsetY={tokenHeight / 2}>
|
||||||
{tokenState.statuses?.length > 0 ? (
|
{tokenState.statuses?.length > 0 ? (
|
||||||
<TokenStatus
|
<TokenStatus
|
||||||
@ -410,8 +485,8 @@ function Token({
|
|||||||
) : null}
|
) : null}
|
||||||
</animated.Group>
|
</animated.Group>
|
||||||
<Transformer
|
<Transformer
|
||||||
active={(!tokenState.locked && selected) || isTransforming}
|
active={transformerActive}
|
||||||
nodeRef={tokenRef}
|
nodes={transformerNodes}
|
||||||
onTransformEnd={handleTransformEnd}
|
onTransformEnd={handleTransformEnd}
|
||||||
onTransformStart={handleTransformStart}
|
onTransformStart={handleTransformStart}
|
||||||
gridScale={map.grid.measurement.scale}
|
gridScale={map.grid.measurement.scale}
|
||||||
|
@ -16,9 +16,9 @@ import scaleDark from "../../images/ScaleDark.png";
|
|||||||
import rotateDark from "../../images/RotateDark.png";
|
import rotateDark from "../../images/RotateDark.png";
|
||||||
import { parseGridScale } from "../../helpers/grid";
|
import { parseGridScale } from "../../helpers/grid";
|
||||||
|
|
||||||
type ResizerProps = {
|
type TransformerProps = {
|
||||||
active: boolean;
|
active: boolean;
|
||||||
nodeRef: React.RefObject<Konva.Node>;
|
nodes: () => Konva.Node[];
|
||||||
onTransformStart?: (event: Konva.KonvaEventObject<Event>) => void;
|
onTransformStart?: (event: Konva.KonvaEventObject<Event>) => void;
|
||||||
onTransformEnd?: (event: Konva.KonvaEventObject<Event>) => void;
|
onTransformEnd?: (event: Konva.KonvaEventObject<Event>) => void;
|
||||||
gridScale: string;
|
gridScale: string;
|
||||||
@ -26,11 +26,11 @@ type ResizerProps = {
|
|||||||
|
|
||||||
function Transformer({
|
function Transformer({
|
||||||
active,
|
active,
|
||||||
nodeRef,
|
nodes,
|
||||||
onTransformStart,
|
onTransformStart,
|
||||||
onTransformEnd,
|
onTransformEnd,
|
||||||
gridScale,
|
gridScale,
|
||||||
}: ResizerProps) {
|
}: TransformerProps) {
|
||||||
const setPreventMapInteraction = useSetPreventMapInteraction();
|
const setPreventMapInteraction = useSetPreventMapInteraction();
|
||||||
|
|
||||||
const gridCellPixelSize = useGridCellPixelSize();
|
const gridCellPixelSize = useGridCellPixelSize();
|
||||||
@ -50,21 +50,25 @@ function Transformer({
|
|||||||
const [anchorRotate, anchorRotateStatus] = useAnchorImage(96, rotateDark);
|
const [anchorRotate, anchorRotateStatus] = useAnchorImage(96, rotateDark);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const transformer = transformerRef.current;
|
||||||
if (
|
if (
|
||||||
active &&
|
active &&
|
||||||
transformerRef.current &&
|
transformer &&
|
||||||
nodeRef.current &&
|
|
||||||
anchorScaleStatus === "loaded" &&
|
anchorScaleStatus === "loaded" &&
|
||||||
anchorRotateStatus === "loaded"
|
anchorRotateStatus === "loaded"
|
||||||
) {
|
) {
|
||||||
// we need to attach transformer manually
|
// we need to attach transformer manually
|
||||||
transformerRef.current.nodes([nodeRef.current]);
|
const n = nodes();
|
||||||
|
// Slice the nodes to only attach a single node to allow useSingleNodeRotation to
|
||||||
|
// control the transformer rotation
|
||||||
|
transformer.setNodes(n.slice(0, 1));
|
||||||
|
// Update the private _nodes to allow transforming all the added nodes
|
||||||
|
// TODO: Look at subclassing Konva.Transformer and remove this hack
|
||||||
|
transformer._nodes = n;
|
||||||
|
|
||||||
const middleLeft =
|
const middleLeft = transformer.findOne<Konva.Rect>(".middle-left");
|
||||||
transformerRef.current.findOne<Konva.Rect>(".middle-left");
|
const middleRight = transformer.findOne<Konva.Rect>(".middle-right");
|
||||||
const middleRight =
|
const rotater = transformer.findOne<Konva.Rect>(".rotater");
|
||||||
transformerRef.current.findOne<Konva.Rect>(".middle-right");
|
|
||||||
const rotater = transformerRef.current.findOne<Konva.Rect>(".rotater");
|
|
||||||
|
|
||||||
middleLeft.fillPriority("pattern");
|
middleLeft.fillPriority("pattern");
|
||||||
middleLeft.fillPatternImage(anchorScale);
|
middleLeft.fillPatternImage(anchorScale);
|
||||||
@ -84,11 +88,11 @@ function Transformer({
|
|||||||
rotater.fillPatternScaleX(0.5);
|
rotater.fillPatternScaleX(0.5);
|
||||||
rotater.fillPatternScaleY(0.5);
|
rotater.fillPatternScaleY(0.5);
|
||||||
|
|
||||||
transformerRef.current.getLayer()?.batchDraw();
|
transformer.getLayer()?.batchDraw();
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
active,
|
active,
|
||||||
nodeRef,
|
nodes,
|
||||||
anchorScale,
|
anchorScale,
|
||||||
anchorRotate,
|
anchorRotate,
|
||||||
anchorScaleStatus,
|
anchorScaleStatus,
|
||||||
@ -97,8 +101,8 @@ function Transformer({
|
|||||||
|
|
||||||
function updateGridCellAbsoluteSize() {
|
function updateGridCellAbsoluteSize() {
|
||||||
if (active) {
|
if (active) {
|
||||||
const node = nodeRef.current;
|
const transformer = transformerRef.current;
|
||||||
const stage = node?.getStage();
|
const stage = transformer?.getStage();
|
||||||
const mapImage = stage?.findOne("#mapImage");
|
const mapImage = stage?.findOne("#mapImage");
|
||||||
if (!mapImage) {
|
if (!mapImage) {
|
||||||
return;
|
return;
|
||||||
@ -123,13 +127,12 @@ function Transformer({
|
|||||||
const transformTextRef = useRef<Konva.Group>();
|
const transformTextRef = useRef<Konva.Group>();
|
||||||
function handleTransformStart(e: Konva.KonvaEventObject<Event>) {
|
function handleTransformStart(e: Konva.KonvaEventObject<Event>) {
|
||||||
const transformer = transformerRef.current;
|
const transformer = transformerRef.current;
|
||||||
const node = nodeRef.current;
|
if (transformer) {
|
||||||
if (transformer && node) {
|
|
||||||
movingAnchorRef.current = transformer._movingAnchorName;
|
movingAnchorRef.current = transformer._movingAnchorName;
|
||||||
setPreventMapInteraction(true);
|
setPreventMapInteraction(true);
|
||||||
|
|
||||||
const transformText = new Konva.Label();
|
const transformText = new Konva.Label();
|
||||||
const stageScale = node.getStage()?.scale() || { x: 1, y: 1 };
|
const stageScale = transformer.getStage()?.scale() || { x: 1, y: 1 };
|
||||||
transformText.scale(Vector2.divide({ x: 1, y: 1 }, stageScale));
|
transformText.scale(Vector2.divide({ x: 1, y: 1 }, stageScale));
|
||||||
|
|
||||||
const tag = new Konva.Tag();
|
const tag = new Konva.Tag();
|
||||||
@ -148,7 +151,7 @@ function Transformer({
|
|||||||
transformText.add(tag);
|
transformText.add(tag);
|
||||||
transformText.add(text);
|
transformText.add(text);
|
||||||
|
|
||||||
node.getLayer()?.add(transformText);
|
transformer.getLayer()?.add(transformText);
|
||||||
transformTextRef.current = transformText;
|
transformTextRef.current = transformText;
|
||||||
|
|
||||||
updateGridCellAbsoluteSize();
|
updateGridCellAbsoluteSize();
|
||||||
@ -159,10 +162,10 @@ function Transformer({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateTransformText() {
|
function updateTransformText() {
|
||||||
const node = nodeRef.current;
|
|
||||||
const movingAnchor = movingAnchorRef.current;
|
const movingAnchor = movingAnchorRef.current;
|
||||||
const transformText = transformTextRef.current;
|
const transformText = transformTextRef.current;
|
||||||
const transformer = transformerRef.current;
|
const transformer = transformerRef.current;
|
||||||
|
const node = transformer?.nodes()[0];
|
||||||
if (node && transformText && transformer) {
|
if (node && transformText && transformer) {
|
||||||
const text = transformText.getChildren()[1] as Konva.Text;
|
const text = transformText.getChildren()[1] as Konva.Text;
|
||||||
if (movingAnchor === "rotater") {
|
if (movingAnchor === "rotater") {
|
||||||
@ -286,7 +289,6 @@ function Transformer({
|
|||||||
anchorCornerRadius={24}
|
anchorCornerRadius={24}
|
||||||
borderStrokeWidth={0}
|
borderStrokeWidth={0}
|
||||||
anchorSize={48}
|
anchorSize={48}
|
||||||
useSingleNodeRotation={true}
|
|
||||||
/>
|
/>
|
||||||
</Portal>
|
</Portal>
|
||||||
);
|
);
|
||||||
|
@ -117,9 +117,23 @@ function useMapTokens(
|
|||||||
useKeyboard(handleKeyDown, handleKeyUp);
|
useKeyboard(handleKeyDown, handleKeyUp);
|
||||||
useBlur(handleBlur);
|
useBlur(handleBlur);
|
||||||
|
|
||||||
|
const [transformingTokensIds, setTransformingTokenIds] = useState<string[]>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
function handleTokenTransformStart(event: Konva.KonvaEventObject<Event>) {
|
||||||
|
const transformer = event.currentTarget as Konva.Transformer;
|
||||||
|
const nodes = transformer.nodes();
|
||||||
|
setTransformingTokenIds(nodes.map((node) => node.id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTokenTransformEnd() {
|
||||||
|
setTransformingTokenIds([]);
|
||||||
|
}
|
||||||
|
|
||||||
function tokenFromTokenState(tokenState: TokenState) {
|
function tokenFromTokenState(tokenState: TokenState) {
|
||||||
return (
|
return (
|
||||||
map && (
|
map &&
|
||||||
|
mapState && (
|
||||||
<Token
|
<Token
|
||||||
key={tokenState.id}
|
key={tokenState.id}
|
||||||
tokenState={tokenState}
|
tokenState={tokenState}
|
||||||
@ -128,6 +142,9 @@ function useMapTokens(
|
|||||||
onTokenMenuClose={handleTokenMenuClose}
|
onTokenMenuClose={handleTokenMenuClose}
|
||||||
onTokenDragStart={handleTokenDragStart}
|
onTokenDragStart={handleTokenDragStart}
|
||||||
onTokenDragEnd={handleTokenDragEnd}
|
onTokenDragEnd={handleTokenDragEnd}
|
||||||
|
onTokenTransformStart={handleTokenTransformStart}
|
||||||
|
onTokenTransformEnd={handleTokenTransformEnd}
|
||||||
|
transforming={transformingTokensIds.includes(tokenState.id)}
|
||||||
draggable={
|
draggable={
|
||||||
selectedToolId === "move" &&
|
selectedToolId === "move" &&
|
||||||
!(tokenState.id in disabledTokens) &&
|
!(tokenState.id in disabledTokens) &&
|
||||||
@ -142,6 +159,7 @@ function useMapTokens(
|
|||||||
tokenState.category !== "prop" && selectedToolId === "drawing"
|
tokenState.category !== "prop" && selectedToolId === "drawing"
|
||||||
}
|
}
|
||||||
map={map}
|
map={map}
|
||||||
|
mapState={mapState}
|
||||||
selected={
|
selected={
|
||||||
!!tokenMenuOptions &&
|
!!tokenMenuOptions &&
|
||||||
isTokenMenuOpen &&
|
isTokenMenuOpen &&
|
||||||
|
@ -38,6 +38,9 @@ export type TokenDragEventHandler = (
|
|||||||
tokenStateId: string,
|
tokenStateId: string,
|
||||||
attachedTokenStateIds: string[]
|
attachedTokenStateIds: string[]
|
||||||
) => void;
|
) => void;
|
||||||
|
export type TokenTransformEventHandler = (
|
||||||
|
event: Konva.KonvaEventObject<Event>
|
||||||
|
) => void;
|
||||||
|
|
||||||
export type NoteCreateEventHander = (notes: Note[]) => void;
|
export type NoteCreateEventHander = (notes: Note[]) => void;
|
||||||
export type NoteRemoveEventHander = (noteIds: string[]) => void;
|
export type NoteRemoveEventHander = (noteIds: string[]) => void;
|
||||||
|
Loading…
Reference in New Issue
Block a user