feat(eraser): implement eraser feature (#221)
* feat(eraser): implement eraser feature Introduces an eraser tool to the freehand drawing plugin, including a new Erase Icon, toolbar button, and hotkey (E). Implements erasing logic in with-freehand-delete.ts and integrates it into the freehand plugin. Updates FreehandShape enum and pointer utilities to support the eraser. * fix(eraser): lint for better readability * fix(eraser): switch the order of eraser icon and freehand icon * fix(eraser): fix multiple suggestions during code review * fix(eraser): make all the freehand elements being erased at the same time in a eraser perform * fix(eraser): fix type declaration and remove unused imports
This commit is contained in:
1 parent
80a65ac469
commit
8cfb1e75a8
7 files changed
+119
-2
No files matched your search
@@ -44,6 +44,12 @@ export const TextIcon = createIcon(
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const EraseIcon = createIcon(
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8.086 2.207a2 2 0 0 1 2.828 0l3.879 3.879a2 2 0 0 1 0 2.828l-5.5 5.5A2 2 0 0 1 7.879 15H5.12a2 2 0 0 1-1.414-.586l-2.5-2.5a2 2 0 0 1 0-2.828zm2.121.707a1 1 0 0 0-1.414 0L4.16 7.547l5.293 5.293 4.633-4.633a1 1 0 0 0 0-1.414zM8.746 13.547 3.453 8.254 1.914 9.793a1 1 0 0 0 0 1.414l2.5 2.5a1 1 0 0 0 .707.293H7.88a1 1 0 0 0 .707-.293z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const StraightArrowLineIcon = createIcon(
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" version="1.1">
|
||||
<g id="straight-line" stroke="none" fill="currentColor">
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
SelectionIcon,
|
||||
ShapeIcon,
|
||||
TextIcon,
|
||||
EraseIcon,
|
||||
StraightArrowLineIcon,
|
||||
FeltTipPenIcon,
|
||||
ImageIcon,
|
||||
@@ -87,6 +88,11 @@ export const BUTTONS: AppToolButtonProps[] = [
|
||||
pointer: FreehandShape.feltTipPen,
|
||||
titleKey: 'toolbar.pen',
|
||||
},
|
||||
{
|
||||
icon: EraseIcon,
|
||||
pointer: FreehandShape.eraser,
|
||||
title: 'Erase — E',
|
||||
},
|
||||
{
|
||||
icon: StraightArrowLineIcon,
|
||||
titleKey: 'toolbar.arrow',
|
||||
|
||||
@@ -29,6 +29,7 @@ export const FreehandThemeColors = {
|
||||
};
|
||||
|
||||
export enum FreehandShape {
|
||||
eraser = 'eraser',
|
||||
nibPen = 'nibPen',
|
||||
feltTipPen = 'feltTipPen',
|
||||
artisticBrush = 'artisticBrush',
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
} from '@plait/draw';
|
||||
|
||||
export function getFreehandPointers() {
|
||||
return [FreehandShape.feltTipPen];
|
||||
return [FreehandShape.feltTipPen, FreehandShape.eraser];
|
||||
}
|
||||
|
||||
export const createFreehandElement = (
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import {
|
||||
PlaitBoard,
|
||||
Point,
|
||||
throttleRAF,
|
||||
toHostPoint,
|
||||
toViewBoxPoint,
|
||||
} from '@plait/core';
|
||||
import { isDrawingMode } from '@plait/common';
|
||||
import { isHitFreehand } from './utils';
|
||||
import { Freehand, FreehandShape } from './type';
|
||||
import { CoreTransforms } from '@plait/core';
|
||||
|
||||
export const withFreehandErase = (board: PlaitBoard) => {
|
||||
const { pointerDown, pointerMove, pointerUp, globalPointerUp } = board;
|
||||
|
||||
let isErasing = false;
|
||||
const elementsToDelete = new Set<string>();
|
||||
|
||||
const checkAndMarkFreehandElementsForDeletion = (point: Point) => {
|
||||
const viewBoxPoint = toViewBoxPoint(board, toHostPoint(board, point[0], point[1]));
|
||||
|
||||
const freehandElements = board.children.filter((element) =>
|
||||
Freehand.isFreehand(element)
|
||||
) as Freehand[];
|
||||
|
||||
freehandElements.forEach((element) => {
|
||||
if (!elementsToDelete.has(element.id) && isHitFreehand(board, element, viewBoxPoint)) {
|
||||
elementsToDelete.add(element.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const deleteMarkedElements = () => {
|
||||
if (elementsToDelete.size > 0) {
|
||||
const elementsToRemove = board.children.filter((element) =>
|
||||
elementsToDelete.has(element.id)
|
||||
);
|
||||
|
||||
if (elementsToRemove.length > 0) {
|
||||
CoreTransforms.removeElements(board, elementsToRemove);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const complete = () => {
|
||||
if (isErasing) {
|
||||
deleteMarkedElements();
|
||||
isErasing = false;
|
||||
elementsToDelete.clear();
|
||||
}
|
||||
};
|
||||
|
||||
board.pointerDown = (event: PointerEvent) => {
|
||||
const isEraserPointer = PlaitBoard.isInPointer(board, [FreehandShape.eraser]);
|
||||
|
||||
if (isEraserPointer && isDrawingMode(board)) {
|
||||
isErasing = true;
|
||||
elementsToDelete.clear();
|
||||
const currentPoint: Point = [event.x, event.y];
|
||||
checkAndMarkFreehandElementsForDeletion(currentPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
pointerDown(event);
|
||||
};
|
||||
|
||||
board.pointerMove = (event: PointerEvent) => {
|
||||
if (isErasing) {
|
||||
throttleRAF(board, 'with-freehand-erase', () => {
|
||||
const currentPoint: Point = [event.x, event.y];
|
||||
checkAndMarkFreehandElementsForDeletion(currentPoint);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
pointerMove(event);
|
||||
};
|
||||
|
||||
board.pointerUp = (event: PointerEvent) => {
|
||||
if (isErasing) {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
|
||||
pointerUp(event);
|
||||
};
|
||||
|
||||
board.globalPointerUp = (event: PointerEvent) => {
|
||||
if (isErasing) {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
|
||||
globalPointerUp(event);
|
||||
};
|
||||
|
||||
return board;
|
||||
};
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
WithDrawOptions,
|
||||
WithDrawPluginKey,
|
||||
} from '@plait/draw';
|
||||
import { withFreehandErase } from './with-freehand-erase';
|
||||
|
||||
export const withFreehand = (board: PlaitBoard) => {
|
||||
const {
|
||||
@@ -83,5 +84,5 @@ export const withFreehand = (board: PlaitBoard) => {
|
||||
{ customGeometryTypes: [FREEHAND_TYPE] }
|
||||
);
|
||||
|
||||
return withFreehandFragment(withFreehandCreate(board));
|
||||
return withFreehandErase(withFreehandFragment(withFreehandCreate(board)));
|
||||
};
|
||||
@@ -61,6 +61,11 @@ export const buildDrawnixHotkeyPlugin = (
|
||||
BoardTransforms.updatePointerType(board, MindPointerType.mind);
|
||||
updateAppState({ pointer: MindPointerType.mind });
|
||||
}
|
||||
if (event.key === 'e') {
|
||||
setCreationMode(board, BoardCreationMode.drawing);
|
||||
BoardTransforms.updatePointerType(board, FreehandShape.eraser);
|
||||
updateAppState({ pointer: FreehandShape.eraser });
|
||||
}
|
||||
if (event.key === 'p') {
|
||||
setCreationMode(board, BoardCreationMode.drawing);
|
||||
BoardTransforms.updatePointerType(board, FreehandShape.feltTipPen);
|
||||
|
||||
Reference in new issue
Block a user