From 98d9b526c8836c0470b4345faf11b991f4cacd35 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 1 Aug 2021 21:00:48 +1000 Subject: [PATCH] Add custom anchors for transformer --- src/components/konva/Transformer.tsx | 68 +++++++++++++++++++++++---- src/images/RotateDark.png | Bin 0 -> 4844 bytes src/images/ScaleDark.png | Bin 0 -> 4076 bytes 3 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 src/images/RotateDark.png create mode 100644 src/images/ScaleDark.png diff --git a/src/components/konva/Transformer.tsx b/src/components/konva/Transformer.tsx index 7c49d80..7350f1d 100644 --- a/src/components/konva/Transformer.tsx +++ b/src/components/konva/Transformer.tsx @@ -1,6 +1,6 @@ import Konva from "konva"; import { Transform } from "konva/lib/Util"; -import { useEffect, useRef } from "react"; +import { useEffect, useMemo, useRef } from "react"; import { Transformer as KonvaTransformer } from "react-konva"; import { useGridCellPixelSize } from "../../contexts/GridContext"; @@ -8,6 +8,9 @@ import { useSetPreventMapInteraction } from "../../contexts/MapInteractionContex import { roundTo } from "../../helpers/shared"; import Vector2 from "../../helpers/Vector2"; +import scaleDark from "../../images/ScaleDark.png"; +import rotateDark from "../../images/RotateDark.png"; + type ResizerProps = { active: boolean; nodeRef: React.RefObject; @@ -25,14 +28,42 @@ function Transformer({ const gridCellPixelSize = useGridCellPixelSize(); + const anchorScale = useMemo(() => getAnchorImage(192, scaleDark), []); + const anchorRotate = useMemo(() => getAnchorImage(192, rotateDark), []); + const transformerRef = useRef(null); useEffect(() => { if (active && transformerRef.current && nodeRef.current) { // we need to attach transformer manually transformerRef.current.nodes([nodeRef.current]); + + const middleLeft = + transformerRef.current.findOne(".middle-left"); + const middleRight = + transformerRef.current.findOne(".middle-right"); + const rotater = transformerRef.current.findOne(".rotater"); + + middleLeft.fillPriority("pattern"); + middleLeft.fillPatternImage(anchorScale); + middleLeft.strokeEnabled(false); + middleLeft.fillPatternScaleX(-0.25); + middleLeft.fillPatternScaleY(0.25); + + middleRight.fillPriority("pattern"); + middleRight.fillPatternImage(anchorScale); + middleRight.strokeEnabled(false); + middleRight.fillPatternScaleX(0.25); + middleRight.fillPatternScaleY(0.25); + + rotater.fillPriority("pattern"); + rotater.fillPatternImage(anchorRotate); + rotater.strokeEnabled(false); + rotater.fillPatternScaleX(0.25); + rotater.fillPatternScaleY(0.25); + transformerRef.current.getLayer()?.batchDraw(); } - }, [active, nodeRef]); + }, [active, nodeRef, anchorScale]); const movingAnchorRef = useRef(); function handleTransformStart(e: Konva.KonvaEventObject) { @@ -132,19 +163,38 @@ function Transformer({ centeredScaling={true} rotationSnaps={[...Array(24).keys()].map((n) => n * 15)} rotateAnchorOffset={20} - anchorCornerRadius={10} enabledAnchors={["middle-left", "middle-right"]} flipEnabled={false} ignoreStroke={true} - borderStroke="transparent" - anchorStroke="hsl(210, 50%, 96%" - anchorFill="hsla(230, 25%, 15%, 80%)" - anchorStrokeWidth={3} - borderStrokeWidth={2} - anchorSize={15} + borderStroke="invisible" + anchorStroke="invisible" + anchorCornerRadius={24} + borderStrokeWidth={0} + anchorSize={48} useSingleNodeRotation={true} /> ); } +function getAnchorImage(size: number, source: string) { + const canvas = document.createElement("canvas"); + canvas.width = size; + canvas.height = size; + const ctx = canvas.getContext("2d"); + const image = new Image(); + image.src = source; + image.onload = () => { + const imageRatio = image.width / image.height; + const imageWidth = canvas.height * imageRatio; + ctx?.drawImage( + image, + canvas.width / 2 - imageWidth / 2, + 0, + imageWidth, + canvas.height + ); + }; + return canvas; +} + export default Transformer; diff --git a/src/images/RotateDark.png b/src/images/RotateDark.png new file mode 100644 index 0000000000000000000000000000000000000000..d8ec78d9d5e94fbcc19aaa901b0f4f1358f72d79 GIT binary patch literal 4844 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 literal 0 HcmV?d00001 diff --git a/src/images/ScaleDark.png b/src/images/ScaleDark.png new file mode 100644 index 0000000000000000000000000000000000000000..20af45d7196addbdba2f907ec160264f67c17fdb GIT binary patch literal 4076 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 literal 0 HcmV?d00001