Я создаю приложение для доски, подобное exelidraw. Я пытаюсь создать инструмент ластик с использованием элемента Canvas, который при столкновении с любой фигурой или рисунком полностью удаляет их, как в excalidraw.
Я пробовал установить свойство globalCompositeOperation CanvasAPI в значение Destination-out, но этот подход удалит фигуру (или рисунок) попиксельно, как в msPaint. Но мне нужен подход на основе объектов, когда ластик удаляет всю фигуру (или рисунок) при столкновении с фигурой (или рисунком) в любой точке.
Ниже приведен код того, как я подход на основе пикселей, который я использовал для создания функциональности ластика.
(просто грубый код для логики ластика)
type Shape = {type: "eraser"; points: {x: number; y: number;}[]};
export class Game {
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
private existingShapes: Shape[];
private clicked: boolean;
private startX = 0;
private startY = 0;
private selectedTool: Tool = "eraser";
private currentPencilStroke: { x: number; y:number }[] = []
constructor(canvas: HTMLCanvasElement) {
this.canvas = canvas;
this.ctx = canvas.getContext("2d")!;
this.existingShapes = [];
this.clicked = false;
this.init();
this.initHandlers();
this.initMouseHandlers();
}
destroy() {
this.canvas.removeEventListener("mousedown", this.mouseDownHandler);
this.canvas.removeEventListener("mouseup", this.mouseUpHandler);
this.canvas.removeEventListener("mousemove", this.mouseMoveHandler);
}
// ClearCanvas(){} -> Clears everything; Redraws the permanent shapes; Draw the current preview shape
clearCanvas() {
this.ctx.globalCompositeOperation = "source-over";
// Clears the entire canvas:
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Sets the background to black:
this.ctx.fillStyle = "rgba(0, 0, 0)";
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
// Redraws Existing Shapes
this.existingShapes.map((shape) => {
this.ctx.globalCompositeOperation = "source-over"
this.ctx.strokeStyle = "rgba(255, 255, 255)";
// Drawing logic of each shape type
if(shape.type === "eraser"){
this.ctx.strokeStyle = "rgba(0, 0, 0)";
this.ctx.lineWidth = 20
if(shape.points && shape.points.length > 1){
this.ctx.beginPath();
this.ctx.moveTo(shape.points[0].x, shape.points[0].y);
shape.points.forEach(point => {
this.ctx.lineTo(point.x, point.y);
});
this.ctx.stroke();
}
}
});
}
// mouseDownHandler: User presses the mouse button; shapes begin to draw; Records where the user started to clicking
mouseDownHandler = (e: MouseEvent) => {
this.clicked = true;
this.startX = e.clientX;
this.startY = e.clientY;
if (this.selectedTool === "eraser") {
this.currentPencilStroke = [{x: this.startX, y: this.startY}]
}
};
// mouseUpHandler: When user release the mouse button, it finalizes the shape and saves it permanently
mouseUpHandler = (e: MouseEvent) => {
this.clicked = false;
const width = e.clientX - this.startX;
const height = e.clientY - this.startY;
const selectedTool = this.selectedTool;
let shape: Shape | null = null;
if(selectedTool === "eraser" && this.currentPencilStroke.length > 1) {
shape = {
type: "eraser",
points: this.currentPencilStroke
}
this.currentPencilStroke = [];
}
// mouseMoveHandler: While user drag the mouse it shows live preview of the shape.
mouseMoveHandler = (e: MouseEvent) => {
if (this.clicked) {
const width = e.clientX - this.startX;
const height = e.clientY - this.startY;
this.clearCanvas();
this.ctx.strokeStyle = "rgba(255, 255, 255)";
const selectedTool: Tool = this.selectedTool;
if(selectedTool === "eraser") {
this.currentPencilStroke.push({x: e.clientX, y: e.clientY})
this.ctx.globalCompositeOperation = "source-over"
this.ctx.strokeStyle = "rgba(0, 0, 0)";
this.ctx.lineWidth = 20
this.ctx.beginPath();
this.ctx.moveTo(this.currentPencilStroke[0].x, this.currentPencilStroke[0].y)
this.currentPencilStroke.forEach(point => {
this.ctx.lineTo(point.x, point.y)
});
this.ctx.stroke()
this.ctx.globalCompositeOperation = "source-over"
}
}
};
initMouseHandlers() {
this.canvas.addEventListener("mousedown", this.mouseDownHandler);
this.canvas.addEventListener("mouseup", this.mouseUpHandler);
this.canvas.addEventListener("mousemove", this.mouseMoveHandler);
}
}
Подробнее здесь: https://stackoverflow.com/questions/798 ... pplication