mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: add eraser tool trail (#7511)
Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
parent
872973f145
commit
86cfeb714c
13 changed files with 482 additions and 356 deletions
|
@ -384,8 +384,7 @@ import { isSidebarDockedAtom } from "./Sidebar/Sidebar";
|
|||
import { StaticCanvas, InteractiveCanvas } from "./canvases";
|
||||
import { Renderer } from "../scene/Renderer";
|
||||
import { ShapeCache } from "../scene/ShapeCache";
|
||||
import { LaserToolOverlay } from "./LaserTool/LaserTool";
|
||||
import { LaserPathManager } from "./LaserTool/LaserPathManager";
|
||||
import { SVGLayer } from "./SVGLayer";
|
||||
import {
|
||||
setEraserCursor,
|
||||
setCursor,
|
||||
|
@ -401,6 +400,10 @@ import { ElementCanvasButton } from "./MagicButton";
|
|||
import { MagicIcon, copyIcon, fullscreenIcon } from "./icons";
|
||||
import { EditorLocalStorage } from "../data/EditorLocalStorage";
|
||||
import FollowMode from "./FollowMode/FollowMode";
|
||||
|
||||
import { AnimationFrameHandler } from "../animation-frame-handler";
|
||||
import { AnimatedTrail } from "../animated-trail";
|
||||
import { LaserTrails } from "../laser-trails";
|
||||
import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
|
||||
import { getRenderOpacity } from "../renderer/renderElement";
|
||||
|
||||
|
@ -537,7 +540,29 @@ class App extends React.Component<AppProps, AppState> {
|
|||
lastPointerMoveEvent: PointerEvent | null = null;
|
||||
lastViewportPosition = { x: 0, y: 0 };
|
||||
|
||||
laserPathManager: LaserPathManager = new LaserPathManager(this);
|
||||
animationFrameHandler = new AnimationFrameHandler();
|
||||
|
||||
laserTrails = new LaserTrails(this.animationFrameHandler, this);
|
||||
eraserTrail = new AnimatedTrail(this.animationFrameHandler, this, {
|
||||
streamline: 0.2,
|
||||
size: 5,
|
||||
keepHead: true,
|
||||
sizeMapping: (c) => {
|
||||
const DECAY_TIME = 200;
|
||||
const DECAY_LENGTH = 10;
|
||||
const t = Math.max(0, 1 - (performance.now() - c.pressure) / DECAY_TIME);
|
||||
const l =
|
||||
(DECAY_LENGTH -
|
||||
Math.min(DECAY_LENGTH, c.totalLength - c.currentIndex)) /
|
||||
DECAY_LENGTH;
|
||||
|
||||
return Math.min(easeOut(l), easeOut(t));
|
||||
},
|
||||
fill: () =>
|
||||
this.state.theme === THEME.LIGHT
|
||||
? "rgba(0, 0, 0, 0.2)"
|
||||
: "rgba(255, 255, 255, 0.2)",
|
||||
});
|
||||
|
||||
onChangeEmitter = new Emitter<
|
||||
[
|
||||
|
@ -1471,7 +1496,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||
<div className="excalidraw-textEditorContainer" />
|
||||
<div className="excalidraw-contextMenuContainer" />
|
||||
<div className="excalidraw-eye-dropper-container" />
|
||||
<LaserToolOverlay manager={this.laserPathManager} />
|
||||
<SVGLayer
|
||||
trails={[this.laserTrails, this.eraserTrail]}
|
||||
/>
|
||||
{selectedElements.length === 1 &&
|
||||
this.state.showHyperlinkPopup && (
|
||||
<Hyperlink
|
||||
|
@ -2394,7 +2421,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.removeEventListeners();
|
||||
this.scene.destroy();
|
||||
this.library.destroy();
|
||||
this.laserPathManager.destroy();
|
||||
this.laserTrails.stop();
|
||||
this.eraserTrail.stop();
|
||||
this.onChangeEmitter.clear();
|
||||
ShapeCache.destroy();
|
||||
SnapCache.destroy();
|
||||
|
@ -2619,6 +2647,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.updateLanguage();
|
||||
}
|
||||
|
||||
if (isEraserActive(prevState) && !isEraserActive(this.state)) {
|
||||
this.eraserTrail.endPath();
|
||||
}
|
||||
|
||||
if (prevProps.viewModeEnabled !== this.props.viewModeEnabled) {
|
||||
this.setState({ viewModeEnabled: !!this.props.viewModeEnabled });
|
||||
}
|
||||
|
@ -5070,6 +5102,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||
pointerDownState: PointerDownState,
|
||||
scenePointer: { x: number; y: number },
|
||||
) => {
|
||||
this.eraserTrail.addPointToPath(scenePointer.x, scenePointer.y);
|
||||
|
||||
let didChange = false;
|
||||
|
||||
const processElements = (elements: ExcalidrawElement[]) => {
|
||||
|
@ -5500,7 +5534,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.state.activeTool.type,
|
||||
);
|
||||
} else if (this.state.activeTool.type === "laser") {
|
||||
this.laserPathManager.startPath(
|
||||
this.laserTrails.startPath(
|
||||
pointerDownState.lastCoords.x,
|
||||
pointerDownState.lastCoords.y,
|
||||
);
|
||||
|
@ -5521,6 +5555,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||
event,
|
||||
);
|
||||
|
||||
if (this.state.activeTool.type === "eraser") {
|
||||
this.eraserTrail.startPath(
|
||||
pointerDownState.lastCoords.x,
|
||||
pointerDownState.lastCoords.y,
|
||||
);
|
||||
}
|
||||
|
||||
const onPointerMove =
|
||||
this.onPointerMoveFromPointerDownHandler(pointerDownState);
|
||||
|
||||
|
@ -6784,7 +6825,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
|
||||
if (this.state.activeTool.type === "laser") {
|
||||
this.laserPathManager.addPointToPath(pointerCoords.x, pointerCoords.y);
|
||||
this.laserTrails.addPointToPath(pointerCoords.x, pointerCoords.y);
|
||||
}
|
||||
|
||||
const [gridX, gridY] = getGridPoint(
|
||||
|
@ -7793,6 +7834,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||
const pointerEnd = this.lastPointerUpEvent || this.lastPointerMoveEvent;
|
||||
|
||||
if (isEraserActive(this.state) && pointerStart && pointerEnd) {
|
||||
this.eraserTrail.endPath();
|
||||
|
||||
const draggedDistance = distance2d(
|
||||
pointerStart.clientX,
|
||||
pointerStart.clientY,
|
||||
|
@ -8041,7 +8084,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
|
||||
if (activeTool.type === "laser") {
|
||||
this.laserPathManager.endPath();
|
||||
this.laserTrails.endPath();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue