Add konva portal for transformer controls
This commit is contained in:
parent
6e7794c38e
commit
1182c9fe1e
@ -46,6 +46,7 @@
|
||||
"react-dom": "^17.0.2",
|
||||
"react-intersection-observer": "^8.32.0",
|
||||
"react-konva": "^17.0.2-5",
|
||||
"react-konva-utils": "^0.1.7",
|
||||
"react-markdown": "4",
|
||||
"react-media": "^2.0.0-rc.1",
|
||||
"react-modal": "^3.12.1",
|
||||
|
@ -2,6 +2,7 @@ import Konva from "konva";
|
||||
import { Transform } from "konva/lib/Util";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Transformer as KonvaTransformer } from "react-konva";
|
||||
import { Portal } from "react-konva-utils";
|
||||
|
||||
import {
|
||||
useGridCellPixelSize,
|
||||
@ -200,80 +201,85 @@ function Transformer({
|
||||
}
|
||||
|
||||
return (
|
||||
<KonvaTransformer
|
||||
ref={transformerRef}
|
||||
boundBoxFunc={(oldBox, newBox) => {
|
||||
let snapBox = { ...newBox };
|
||||
const movingAnchor = movingAnchorRef.current;
|
||||
if (movingAnchor === "middle-left" || movingAnchor === "middle-right") {
|
||||
// Account for grid snapping
|
||||
const nearestCellWidth = roundTo(
|
||||
snapBox.width,
|
||||
gridCellAbsoluteSize.x
|
||||
);
|
||||
const distanceToSnap = Math.abs(snapBox.width - nearestCellWidth);
|
||||
let snapping = false;
|
||||
<Portal selector="#portal" enabled>
|
||||
<KonvaTransformer
|
||||
ref={transformerRef}
|
||||
boundBoxFunc={(oldBox, newBox) => {
|
||||
let snapBox = { ...newBox };
|
||||
const movingAnchor = movingAnchorRef.current;
|
||||
if (
|
||||
distanceToSnap <
|
||||
gridCellAbsoluteSize.x * gridSnappingSensitivity
|
||||
movingAnchor === "middle-left" ||
|
||||
movingAnchor === "middle-right"
|
||||
) {
|
||||
snapBox.width = nearestCellWidth;
|
||||
snapping = true;
|
||||
// Account for grid snapping
|
||||
const nearestCellWidth = roundTo(
|
||||
snapBox.width,
|
||||
gridCellAbsoluteSize.x
|
||||
);
|
||||
const distanceToSnap = Math.abs(snapBox.width - nearestCellWidth);
|
||||
let snapping = false;
|
||||
if (
|
||||
distanceToSnap <
|
||||
gridCellAbsoluteSize.x * gridSnappingSensitivity
|
||||
) {
|
||||
snapBox.width = nearestCellWidth;
|
||||
snapping = true;
|
||||
}
|
||||
|
||||
const deltaWidth = snapBox.width - oldBox.width;
|
||||
// Account for node ratio
|
||||
const inverseRatio =
|
||||
Math.round(oldBox.height) / Math.round(oldBox.width);
|
||||
const deltaHeight = inverseRatio * deltaWidth;
|
||||
|
||||
// Account for node rotation
|
||||
// Create a transform to unrotate the x,y position of the Box
|
||||
const rotator = new Transform();
|
||||
rotator.rotate(-snapBox.rotation);
|
||||
|
||||
// Unrotate and add the resize amount
|
||||
let rotatedMin = rotator.point({ x: snapBox.x, y: snapBox.y });
|
||||
rotatedMin.y = rotatedMin.y - deltaHeight / 2;
|
||||
// Snap x position if needed
|
||||
if (snapping) {
|
||||
const snapDelta = newBox.width - nearestCellWidth;
|
||||
rotatedMin.x = rotatedMin.x + snapDelta / 2;
|
||||
}
|
||||
|
||||
// Rotated back
|
||||
rotator.invert();
|
||||
rotatedMin = rotator.point(rotatedMin);
|
||||
|
||||
snapBox = {
|
||||
...snapBox,
|
||||
height: snapBox.height + deltaHeight,
|
||||
x: rotatedMin.x,
|
||||
y: rotatedMin.y,
|
||||
};
|
||||
}
|
||||
|
||||
const deltaWidth = snapBox.width - oldBox.width;
|
||||
// Account for node ratio
|
||||
const inverseRatio =
|
||||
Math.round(oldBox.height) / Math.round(oldBox.width);
|
||||
const deltaHeight = inverseRatio * deltaWidth;
|
||||
|
||||
// Account for node rotation
|
||||
// Create a transform to unrotate the x,y position of the Box
|
||||
const rotator = new Transform();
|
||||
rotator.rotate(-snapBox.rotation);
|
||||
|
||||
// Unrotate and add the resize amount
|
||||
let rotatedMin = rotator.point({ x: snapBox.x, y: snapBox.y });
|
||||
rotatedMin.y = rotatedMin.y - deltaHeight / 2;
|
||||
// Snap x position if needed
|
||||
if (snapping) {
|
||||
const snapDelta = newBox.width - nearestCellWidth;
|
||||
rotatedMin.x = rotatedMin.x + snapDelta / 2;
|
||||
if (snapBox.width < 5 || snapBox.height < 5) {
|
||||
return oldBox;
|
||||
}
|
||||
|
||||
// Rotated back
|
||||
rotator.invert();
|
||||
rotatedMin = rotator.point(rotatedMin);
|
||||
|
||||
snapBox = {
|
||||
...snapBox,
|
||||
height: snapBox.height + deltaHeight,
|
||||
x: rotatedMin.x,
|
||||
y: rotatedMin.y,
|
||||
};
|
||||
}
|
||||
|
||||
if (snapBox.width < 5 || snapBox.height < 5) {
|
||||
return oldBox;
|
||||
}
|
||||
return snapBox;
|
||||
}}
|
||||
onTransformStart={handleTransformStart}
|
||||
onTransform={handleTrasform}
|
||||
onTransformEnd={handleTransformEnd}
|
||||
centeredScaling={true}
|
||||
rotationSnaps={[...Array(24).keys()].map((n) => n * 15)}
|
||||
rotateAnchorOffset={16}
|
||||
enabledAnchors={["middle-left", "middle-right"]}
|
||||
flipEnabled={false}
|
||||
ignoreStroke={true}
|
||||
borderStroke="invisible"
|
||||
anchorStroke="invisible"
|
||||
anchorCornerRadius={24}
|
||||
borderStrokeWidth={0}
|
||||
anchorSize={48}
|
||||
useSingleNodeRotation={true}
|
||||
/>
|
||||
return snapBox;
|
||||
}}
|
||||
onTransformStart={handleTransformStart}
|
||||
onTransform={handleTrasform}
|
||||
onTransformEnd={handleTransformEnd}
|
||||
centeredScaling={true}
|
||||
rotationSnaps={[...Array(24).keys()].map((n) => n * 15)}
|
||||
rotateAnchorOffset={16}
|
||||
enabledAnchors={["middle-left", "middle-right"]}
|
||||
flipEnabled={false}
|
||||
ignoreStroke={true}
|
||||
borderStroke="invisible"
|
||||
anchorStroke="invisible"
|
||||
anchorCornerRadius={24}
|
||||
borderStrokeWidth={0}
|
||||
anchorSize={48}
|
||||
useSingleNodeRotation={true}
|
||||
/>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useRef, useEffect, useState } from "react";
|
||||
import { Box } from "theme-ui";
|
||||
import ReactResizeDetector from "react-resize-detector";
|
||||
import { Stage, Layer, Image } from "react-konva";
|
||||
import { Stage, Layer, Image, Group } from "react-konva";
|
||||
import Konva from "konva";
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
@ -215,8 +215,8 @@ function MapInteraction({
|
||||
id="mapImage"
|
||||
ref={mapImageRef}
|
||||
/>
|
||||
|
||||
{mapLoaded && children}
|
||||
<Group id="portal" />
|
||||
</Layer>
|
||||
</KonvaBridge>
|
||||
</ReactResizeDetector>
|
||||
|
10
yarn.lock
10
yarn.lock
@ -11254,7 +11254,15 @@ react-is@^17.0.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
||||
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
|
||||
|
||||
react-konva@^17.0.2-5:
|
||||
react-konva-utils@^0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/react-konva-utils/-/react-konva-utils-0.1.7.tgz#f3feb6c0dd5a930020fd10a0f57d5c7a904166c8"
|
||||
integrity sha512-wjNq4TsPXaig2Zufg6nBeFQpijR3FJ4iL2YrmtlwQN7Ase158eWHrpHKNySdMo2E4OSls8220sIaTY5TlrzqyA==
|
||||
dependencies:
|
||||
react-konva "^17.0.2-4"
|
||||
use-image "^1.0.7"
|
||||
|
||||
react-konva@^17.0.2-4, react-konva@^17.0.2-5:
|
||||
version "17.0.2-5"
|
||||
resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-17.0.2-5.tgz#e70b0acf323402de0a540f27b300fbe7ed151849"
|
||||
integrity sha512-IyzdfqRDK8r1ulp/jbLPX18AuO+n5yNtL0+4T0QEUsgArRqIl/VRCG1imA5mYJBk0cBNC5+fWDHN+HWEW62ZEQ==
|
||||
|
Loading…
x
Reference in New Issue
Block a user