在Angular中使用P5.js实现图形拖拽功能的问题排查
问题:Canvas图形拖拽时数值更新但图形不动,拖拽路径残留
我尝试实现Canvas上图形的拖拽移动功能,尽管rectX和rectY的数值已经更新,但图形始终固定不动。之前试过在draw()中直接用s.mouseX处理,结果拖拽时会留下路径残留,效果混乱。
原代码:
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import * as p5 from 'p5'; import { DrawingService } from '../services/drawing-service.services'; @Component({ selector: 'app-drawing-canvas', templateUrl: './drawing-canvas.component.html', styleUrls: ['./drawing-canvas.component.scss'] }) export class DrawingCanvasComponent implements OnInit { private p5: any; public canvas: any; public points: any[] = []; public sw = 2; public c: any[] = []; public strokeColor = 0; public currentTool!: string; public currentColor!: string; public currentThickness!: number; public currentSize!: number; public isDraggable: boolean = false; public rectX: number = 100; public rectY: number = 300; public dragPoint: any; public shapes: any[] = []; public isRectSelected: boolean = false; constructor(private drawingService: DrawingService) { this.drawingService.toolChanged.subscribe(tool => { this.currentTool = tool; }); this.drawingService.colorChanged.subscribe(color => { this.currentColor = color; }); this.drawingService.thicknessChanged.subscribe(thickness => { this.currentThickness = thickness; }); this.drawingService.sizeChanged.subscribe(size => { this.currentSize = size; }); } ngOnInit(): void { const sketch = (s: any) => { s.setup = () => { s.createCanvas(s.windowWidth - 200, s.windowHeight - 200).parent('sketch-holder'); s.background(255); s.strokeWeight(this.sw); this.c[0] = s.color(255, 0, 0); // red //s.rect(0, 0, s.width, s.height); s.stroke(this.c[this.strokeColor]); }; s.draw = () => { if (this.currentTool === 'Pen'){ if (s.mouseIsPressed) { if (s.mouseButton === s.LEFT) { s.line(s.mouseX, s.mouseY, s.pmouseX, s.pmouseY); } else if (s.mouseButton === s.CENTER) { s.background(255); } } } if(this.currentTool === 'Rectangle') { // draw a rectangle on the canvas if (s.mouseIsPressed) { if (s.mouseButton === s.LEFT) { s.drawRectangle(this.rectX, this.rectY, 200, 100); }else if (s.mouseButton === s.CENTER) { s.background(255); } } } if(this.currentTool === 'Hand') { console.log(this.currentTool) s.mouseClicked = () => { // Check if the mouse is inside the rectangle console.log(s.mouseX > this.rectX && s.mouseX < this.rectX + 200 && s.mouseY > this.rectY && s.mouseY < this.rectY + 100) // if (s.mouseX !== null && s.mouseY !== null && s.mouseX !== this.rectX && s.mouseY !== this.rectY) { // Start dragging the rectangle this.isRectSelected = true; this.dragPoint = { x: s.mouseX, y: s.mouseY }; } }; // Update the rectangle's position if the mouse has moved while it is being dragged s.mouseMoved = () => { if (this.isRectSelected && s.mouseX > 0 && s.mouseX < s.width && s.mouseY > 0 && s.mouseY < s.height) { if (s.mouseX !== this.dragPoint.x || s.mouseY !== this.dragPoint.y) { this.rectX += s.mouseX - this.dragPoint.x; this.rectY += s.mouseY - this.dragPoint.y; this.dragPoint = { x: s.mouseX, y: s.mouseY }; } } }; s.mouseReleased = () => { // Stop dragging the rectangle this.isRectSelected = false; }; } }; s.drawRectangle = (x: number, y: number, w: number, h: number) => { return this.shapes.push(s.rect(x, y, w, h)); } s.keyPressed = () => { if (s.key === 'c') { window.location.reload(); } }; }; this.canvas = new p5(sketch); } }
解决方案与修改说明
问题根源在于事件绑定逻辑错误、绘制时机不对,以及拖拽位置计算有误。以下是修复后的代码和关键改动:
修复后代码
import { Component, OnInit } from '@angular/core'; import * as p5 from 'p5'; import { DrawingService } from '../services/drawing-service.services'; @Component({ selector: 'app-drawing-canvas', templateUrl: './drawing-canvas.component.html', styleUrls: ['./drawing-canvas.component.scss'] }) export class DrawingCanvasComponent implements OnInit { private p5: any; public canvas: any; public sw = 2; public c: any[] = []; public strokeColor = 0; public currentTool!: string; public rectX: number = 100; public rectY: number = 300; public dragOffsetX: number = 0; public dragOffsetY: number = 0; public isRectSelected: boolean = false; constructor(private drawingService: DrawingService) { this.drawingService.toolChanged.subscribe(tool => this.currentTool = tool); this.drawingService.colorChanged.subscribe(color => this.currentColor = color); this.drawingService.thicknessChanged.subscribe(thickness => this.currentThickness = thickness); this.drawingService.sizeChanged.subscribe(size => this.currentSize = size); } ngOnInit(): void { const sketch = (s: any) => { s.setup = () => { s.createCanvas(s.windowWidth - 200, s.windowHeight - 200).parent('sketch-holder'); s.background(255); s.strokeWeight(this.sw); this.c[0] = s.color(255, 0, 0); s.stroke(this.c[this.strokeColor]); }; // 事件绑定移至draw外部,避免重复绑定 s.mouseClicked = () => { if (this.currentTool !== 'Hand') return; // 判断点击是否在矩形范围内 const isInRect = s.mouseX > this.rectX && s.mouseX < this.rectX + 200 && s.mouseY > this.rectY && s.mouseY < this.rectY + 100; this.isRectSelected = isInRect; if (isInRect) { // 记录鼠标与矩形左上角的偏移量,防止拖拽跳变 this.dragOffsetX = s.mouseX - this.rectX; this.dragOffsetY = s.mouseY - this.rectY; } }; s.mouseDragged = () => { if (this.currentTool !== 'Hand' || !this.isRectSelected) return; // 根据偏移量计算新位置 this.rectX = s.mouseX - this.dragOffsetX; this.rectY = s.mouseY - this.dragOffsetY; }; s.mouseReleased = () => { this.isRectSelected = false; }; s.draw = () => { // 每次绘制前清屏,解决拖拽残留问题 s.background(255); // 画笔工具逻辑 if (this.currentTool === 'Pen' && s.mouseIsPressed) { if (s.mouseButton === s.LEFT) { s.line(s.mouseX, s.mouseY, s.pmouseX, s.pmouseY); } else if (s.mouseButton === s.CENTER) { s.background(255); } } // 矩形工具:点击绘制固定位置矩形 if (this.currentTool === 'Rectangle' && s.mouseIsPressed && s.mouseButton === s.LEFT) { s.rect(this.rectX, this.rectY, 200, 100); } // 拖拽工具:持续绘制矩形以展示实时位置 if (this.currentTool === 'Hand') { s.rect(this.rectX, this.rectY, 200, 100); } }; s.keyPressed = () => { if (s.key === 'c') window.location.reload(); }; }; this.canvas = new p5(sketch); } }
关键改动点
- 事件绑定位置调整:将
mouseClicked、mouseDragged、mouseReleased移出draw()函数,避免每次draw循环重复绑定事件导致逻辑冲突。 - 拖拽逻辑修正:用
dragOffsetX/Y记录鼠标点击点与矩形左上角的偏移量,拖拽时通过当前鼠标位置 - 偏移量计算新位置,解决点击瞬间的位置跳变问题。 - 清屏与持续绘制:在
draw()开头添加s.background(255)清屏,消除拖拽残留;同时在Hand工具下持续绘制矩形,确保位置更新后图形实时刷新。 - 绘制逻辑简化:移除
shapes数组的无效存储(原代码存储的是p5.rect的返回值,无法用于重绘),直接根据当前位置绘制矩形。
内容的提问来源于stack exchange,提问作者Curtis Lanz
相关产品推荐
相关产品推荐

