You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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);
  }
}

关键改动点

  1. 事件绑定位置调整:将mouseClicked、mouseDragged、mouseReleased移出draw()函数,避免每次draw循环重复绑定事件导致逻辑冲突。
  2. 拖拽逻辑修正:用dragOffsetX/Y记录鼠标点击点与矩形左上角的偏移量,拖拽时通过当前鼠标位置 - 偏移量计算新位置,解决点击瞬间的位置跳变问题。
  3. 清屏与持续绘制:在draw()开头添加s.background(255)清屏,消除拖拽残留;同时在Hand工具下持续绘制矩形,确保位置更新后图形实时刷新。
  4. 绘制逻辑简化:移除shapes数组的无效存储(原代码存储的是p5.rect的返回值,无法用于重绘),直接根据当前位置绘制矩形。

内容的提问来源于stack exchange,提问作者Curtis Lanz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:10:36