Add dash animation to selection and change deselect behaviour

This commit is contained in:
Mitchell McCaffrey 2021-07-22 12:10:56 +10:00
parent 4cee11d5ea
commit 648d308fa5
2 changed files with 82 additions and 11 deletions

View File

@ -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}
/>
);
}

View File

@ -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>