Refactor konva components and map tools
This commit is contained in:
@@ -6,7 +6,7 @@ import { useDataURL } from "../../contexts/AssetsContext";
|
||||
import { tokenSources as defaultTokenSources } from "../../tokens";
|
||||
import { Token } from "../../types/Token";
|
||||
|
||||
import { TokenOutlineSVG } from "./TokenOutline";
|
||||
import TokenOutlineSVG from "./TokenOutline";
|
||||
|
||||
type TokenImageProps = {
|
||||
token: Token;
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
import Konva from "konva";
|
||||
import { useRef, useEffect, useState } from "react";
|
||||
import { Rect, Text, Group } from "react-konva";
|
||||
|
||||
import useSetting from "../../hooks/useSetting";
|
||||
import { TokenState } from "../../types/TokenState";
|
||||
|
||||
const maxTokenSize = 3;
|
||||
const defaultFontSize = 16;
|
||||
|
||||
type TokenLabelProps = {
|
||||
tokenState: TokenState;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
function TokenLabel({ tokenState, width, height }: TokenLabelProps) {
|
||||
const [labelSize] = useSetting<number>("map.labelSize");
|
||||
|
||||
const paddingY =
|
||||
(height / 12 / tokenState.size) * Math.min(tokenState.size, maxTokenSize);
|
||||
const paddingX =
|
||||
(height / 8 / tokenState.size) * Math.min(tokenState.size, maxTokenSize);
|
||||
|
||||
const [fontScale, setFontScale] = useState(0);
|
||||
useEffect(() => {
|
||||
const text = textSizerRef.current;
|
||||
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fontSizes: number[] = [];
|
||||
for (let size = 20 * labelSize; size >= 6; size--) {
|
||||
const verticalSize = height / size / tokenState.size;
|
||||
const tokenSize = Math.min(tokenState.size, maxTokenSize);
|
||||
const fontSize = verticalSize * tokenSize * labelSize;
|
||||
fontSizes.push(fontSize);
|
||||
}
|
||||
|
||||
const findFontScale = () => {
|
||||
const size = fontSizes.reduce((prev, curr) => {
|
||||
text.fontSize(curr);
|
||||
const textWidth = text.getTextWidth() + paddingX * 2;
|
||||
if (textWidth < width) {
|
||||
return curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 1);
|
||||
|
||||
setFontScale(size / defaultFontSize);
|
||||
};
|
||||
|
||||
findFontScale();
|
||||
}, [
|
||||
tokenState.label,
|
||||
tokenState.visible,
|
||||
width,
|
||||
height,
|
||||
tokenState,
|
||||
labelSize,
|
||||
paddingX,
|
||||
]);
|
||||
|
||||
const [rectWidth, setRectWidth] = useState(0);
|
||||
const [textWidth, setTextWidth] = useState(0);
|
||||
useEffect(() => {
|
||||
const text = textRef.current;
|
||||
if (text && tokenState.label) {
|
||||
setRectWidth(text.getTextWidth() * fontScale + paddingX * 2);
|
||||
setTextWidth(text.getTextWidth() * fontScale);
|
||||
} else {
|
||||
setRectWidth(0);
|
||||
setTextWidth(0);
|
||||
}
|
||||
}, [tokenState.label, paddingX, width, fontScale]);
|
||||
|
||||
const textRef = useRef<Konva.Text>(null);
|
||||
const textSizerRef = useRef<Konva.Text>(null);
|
||||
|
||||
return (
|
||||
<Group y={height - (defaultFontSize * fontScale + paddingY) / 2}>
|
||||
<Rect
|
||||
y={-paddingY / 2}
|
||||
width={rectWidth}
|
||||
offsetX={width / 2}
|
||||
x={width - rectWidth / 2}
|
||||
height={defaultFontSize * fontScale + paddingY}
|
||||
fill="hsla(230, 25%, 18%, 0.8)"
|
||||
cornerRadius={(defaultFontSize * fontScale + paddingY) / 2}
|
||||
/>
|
||||
<Group offsetX={(textWidth - width) / 2}>
|
||||
<Text
|
||||
ref={textRef}
|
||||
text={tokenState.label}
|
||||
fontSize={defaultFontSize}
|
||||
lineHeight={1}
|
||||
// Scale font instead of changing font size to avoid kerning issues with Firefox
|
||||
scaleX={fontScale}
|
||||
scaleY={fontScale}
|
||||
fill="white"
|
||||
wrap="none"
|
||||
ellipsis={false}
|
||||
hitFunc={() => {}}
|
||||
/>
|
||||
</Group>
|
||||
{/* Use an invisible text block to work out text sizing */}
|
||||
<Text
|
||||
visible={false}
|
||||
ref={textSizerRef}
|
||||
text={tokenState.label}
|
||||
wrap="none"
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
export default TokenLabel;
|
||||
@@ -1,6 +1,3 @@
|
||||
import { Rect, Circle, Line } from "react-konva";
|
||||
|
||||
import colors from "../../helpers/colors";
|
||||
import { Outline } from "../../types/Outline";
|
||||
|
||||
type TokenOutlineSVGProps = {
|
||||
@@ -65,45 +62,4 @@ export function TokenOutlineSVG({
|
||||
}
|
||||
}
|
||||
|
||||
type TokenOutlineProps = {
|
||||
outline: Outline;
|
||||
hidden: boolean;
|
||||
};
|
||||
|
||||
function TokenOutline({ outline, hidden }: TokenOutlineProps) {
|
||||
const sharedProps = {
|
||||
fill: colors.black,
|
||||
opacity: hidden ? 0 : 0.8,
|
||||
};
|
||||
if (outline.type === "rect") {
|
||||
return (
|
||||
<Rect
|
||||
width={outline.width}
|
||||
height={outline.height}
|
||||
x={outline.x}
|
||||
y={outline.y}
|
||||
{...sharedProps}
|
||||
/>
|
||||
);
|
||||
} else if (outline.type === "circle") {
|
||||
return (
|
||||
<Circle
|
||||
radius={outline.radius}
|
||||
x={outline.x}
|
||||
y={outline.y}
|
||||
{...sharedProps}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Line
|
||||
points={outline.points}
|
||||
closed
|
||||
tension={outline.points.length < 200 ? 0 : 0.33}
|
||||
{...sharedProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TokenOutline;
|
||||
export default TokenOutlineSVG;
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Circle, Group } from "react-konva";
|
||||
|
||||
import colors from "../../helpers/colors";
|
||||
import { TokenState } from "../../types/TokenState";
|
||||
|
||||
type TokenStatusProps = {
|
||||
tokenState: TokenState;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
function TokenStatus({ tokenState, width, height }: TokenStatusProps) {
|
||||
// Ensure statuses is an array and filter empty values
|
||||
const statuses = [...new Set((tokenState?.statuses || []).filter((s) => s))];
|
||||
return (
|
||||
<Group x={width} y={height} offsetX={width / 2} offsetY={height / 2}>
|
||||
{statuses.map((status, index) => (
|
||||
<Circle
|
||||
key={status}
|
||||
width={width}
|
||||
height={height}
|
||||
stroke={colors[status]}
|
||||
strokeWidth={width / 20 / tokenState.size}
|
||||
scaleX={1 - index / 10 / tokenState.size}
|
||||
scaleY={1 - index / 10 / tokenState.size}
|
||||
opacity={0.8}
|
||||
fillEnabled={false}
|
||||
/>
|
||||
))}
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
export default TokenStatus;
|
||||
Reference in New Issue
Block a user