Add dash animation to selection and change deselect behaviour
This commit is contained in:
parent
4cee11d5ea
commit
648d308fa5
@ -16,22 +16,26 @@ import {
|
||||
Selection as SelectionType,
|
||||
SelectionItemType,
|
||||
} from "../../types/Select";
|
||||
import { useRef } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import Vector2 from "../../helpers/Vector2";
|
||||
import { SelectionItemsChangeEventHandler } from "../../types/Events";
|
||||
import { TokenState } from "../../types/TokenState";
|
||||
import { Note } from "../../types/Note";
|
||||
|
||||
const dashAnimationSpeed = -0.01;
|
||||
|
||||
type SelectionProps = {
|
||||
selection: SelectionType;
|
||||
onSelectionChange: (selection: SelectionType | null) => void;
|
||||
onSelectionItemsChange: SelectionItemsChangeEventHandler;
|
||||
onPreventSelectionChange: (preventSelection: boolean) => void;
|
||||
} & Konva.ShapeConfig;
|
||||
|
||||
function Selection({
|
||||
selection,
|
||||
onSelectionChange,
|
||||
onSelectionItemsChange,
|
||||
onPreventSelectionChange,
|
||||
...props
|
||||
}: SelectionProps) {
|
||||
const userId = useUserId();
|
||||
@ -97,26 +101,69 @@ function Selection({
|
||||
y: event.target.y() / mapHeight,
|
||||
});
|
||||
intersectingNodesRef.current = [];
|
||||
onPreventSelectionChange(false);
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
onSelectionChange(null);
|
||||
function handlePointerDown() {
|
||||
onPreventSelectionChange(true);
|
||||
}
|
||||
|
||||
const strokeWidth = gridStrokeWidth / stageScale;
|
||||
function handlePointerUp() {
|
||||
onPreventSelectionChange(false);
|
||||
}
|
||||
|
||||
const hasItems = selection.items.length > 0;
|
||||
|
||||
const requestRef = useRef<number>();
|
||||
const lineRef = useRef<Konva.Line>(null);
|
||||
const rectRef = useRef<Konva.Rect>(null);
|
||||
useEffect(() => {
|
||||
let prevTime = performance.now();
|
||||
function animate(time: number) {
|
||||
const delta = time - prevTime;
|
||||
prevTime = time;
|
||||
if (!hasItems) {
|
||||
return;
|
||||
}
|
||||
requestRef.current = requestAnimationFrame(animate);
|
||||
if (lineRef.current) {
|
||||
lineRef.current.dashOffset(
|
||||
lineRef.current.dashOffset() + delta * dashAnimationSpeed
|
||||
);
|
||||
}
|
||||
if (rectRef.current) {
|
||||
rectRef.current.dashOffset(
|
||||
rectRef.current.dashOffset() + delta * dashAnimationSpeed
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
requestRef.current = requestAnimationFrame(animate);
|
||||
|
||||
return () => {
|
||||
if (requestRef.current !== undefined) {
|
||||
cancelAnimationFrame(requestRef.current);
|
||||
}
|
||||
};
|
||||
}, [hasItems]);
|
||||
|
||||
const strokeWidth = (gridStrokeWidth * 0.75) / stageScale;
|
||||
const defaultProps = {
|
||||
stroke: colors.primary,
|
||||
strokeWidth: strokeWidth,
|
||||
dash: [strokeWidth / 2, strokeWidth * 2],
|
||||
dash: hasItems ? [strokeWidth / 2, strokeWidth * 2] : [],
|
||||
onDragStart: handleDragStart,
|
||||
onDragMove: handleDragMove,
|
||||
onDragEnd: handleDragEnd,
|
||||
draggable: true,
|
||||
onClick: handleClick,
|
||||
onTap: handleClick,
|
||||
onMouseDown: handlePointerDown,
|
||||
onMouseUp: handlePointerUp,
|
||||
onTouchStart: handlePointerDown,
|
||||
onTouchEnd: handlePointerUp,
|
||||
};
|
||||
const x = selection.x * mapWidth;
|
||||
const y = selection.y * mapHeight;
|
||||
|
||||
if (selection.type === "path") {
|
||||
return (
|
||||
<Line
|
||||
@ -125,13 +172,14 @@ function Selection({
|
||||
y: mapHeight,
|
||||
})}
|
||||
tension={0.5}
|
||||
closed={selection.items.length > 0}
|
||||
closed={hasItems}
|
||||
lineCap="round"
|
||||
lineJoin="round"
|
||||
x={x}
|
||||
y={y}
|
||||
{...defaultProps}
|
||||
{...props}
|
||||
ref={lineRef}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@ -147,6 +195,7 @@ function Selection({
|
||||
lineJoin="round"
|
||||
{...defaultProps}
|
||||
{...props}
|
||||
ref={rectRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { Group } from "react-konva";
|
||||
|
||||
import {
|
||||
@ -54,6 +54,10 @@ function SelectTool({
|
||||
const [selection, setSelection] = useState<SelectionType | null>(null);
|
||||
const [isBrushDown, setIsBrushDown] = useState(false);
|
||||
|
||||
// Use a ref here to prevent case where brush down event
|
||||
// would fire before React state was refreshed
|
||||
const preventSelectionRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!active) {
|
||||
return;
|
||||
@ -77,7 +81,7 @@ function SelectTool({
|
||||
|
||||
function handleBrushDown() {
|
||||
const brushPosition = getBrushPosition();
|
||||
if (!brushPosition || selection) {
|
||||
if (!brushPosition || preventSelectionRef.current) {
|
||||
return;
|
||||
}
|
||||
if (toolSettings.type === "path") {
|
||||
@ -102,7 +106,10 @@ function SelectTool({
|
||||
|
||||
function handleBrushMove() {
|
||||
const brushPosition = getBrushPosition();
|
||||
if (isBrushDown && selection && brushPosition && mapImage) {
|
||||
if (!brushPosition || preventSelectionRef.current) {
|
||||
return;
|
||||
}
|
||||
if (isBrushDown && selection && mapImage) {
|
||||
if (selection.type === "path") {
|
||||
setSelection((prevSelection) => {
|
||||
if (prevSelection?.type !== "path") {
|
||||
@ -149,6 +156,9 @@ function SelectTool({
|
||||
}
|
||||
|
||||
function handleBrushUp() {
|
||||
if (preventSelectionRef.current) {
|
||||
return;
|
||||
}
|
||||
if (selection && mapStage) {
|
||||
const tokensGroup = mapStage.findOne<Konva.Group>("#tokens");
|
||||
const notesGroup = mapStage.findOne<Konva.Group>("#notes");
|
||||
@ -223,14 +233,23 @@ function SelectTool({
|
||||
setIsBrushDown(false);
|
||||
}
|
||||
|
||||
function handlePointerClick() {
|
||||
if (preventSelectionRef.current) {
|
||||
return;
|
||||
}
|
||||
setSelection(null);
|
||||
}
|
||||
|
||||
interactionEmitter?.on("dragStart", handleBrushDown);
|
||||
interactionEmitter?.on("drag", handleBrushMove);
|
||||
interactionEmitter?.on("dragEnd", handleBrushUp);
|
||||
mapStage?.on("click tap", handlePointerClick);
|
||||
|
||||
return () => {
|
||||
interactionEmitter?.off("dragStart", handleBrushDown);
|
||||
interactionEmitter?.off("drag", handleBrushMove);
|
||||
interactionEmitter?.off("dragEnd", handleBrushUp);
|
||||
mapStage?.off("click tap", handlePointerClick);
|
||||
};
|
||||
});
|
||||
|
||||
@ -241,6 +260,9 @@ function SelectTool({
|
||||
selection={selection}
|
||||
onSelectionChange={setSelection}
|
||||
onSelectionItemsChange={onSelectionItemsChange}
|
||||
onPreventSelectionChange={(prevent: boolean) =>
|
||||
(preventSelectionRef.current = prevent)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
|
Loading…
Reference in New Issue
Block a user