【问题标题】:HTML 5 canvas drag and drop upon multiple objectsHTML 5 画布拖放到多个对象上
【发布时间】:2018-01-16 13:36:40
【问题描述】:

我有一个自定义代码可以在 HTML5 画布上创建和呈现对象。

class Rectangle extends Shape {

  constructor(options, canvas, type = 'rectangle') {
    super(...); // derived from super class

    this._options = options;
    this._canvas = canvas;
    this._context = canvas.context2D;
    this._type = type;

    this._hovered = false;
    this._dragged = false;

    this.init();

    this.canvas.canvas.onmousemove = (e) => {
      this.onMouseHover(e);
    };

    this.canvas.canvas.onmousedown = (e) => {
      this.onMouseDrag(e);
      console.log('dragging');
    }

    this.canvas.canvas.onmouseup = (e) => {
      this.onMouseRelease(e);
      console.log('release');
    }
  }

  static draw(options, canvas) {
    return new Rectangle(options, canvas);
  }

  // Getter and setter


  init() {
    this.drawRectangle(...);
  }

  onMouseHover(e) {
    if( // mouse is above of the shape position 
      e.clientX >= this.x &&
      e.clientX <= this.x + this.width &&
      e.clientY >= this.y &&
      e.clientY <= this.y + this.height
    ) {
      this.drawRectangle(...) // inverse the border color with background
      this.canvas.canvas.style.cursor = 'pointer';
    } else {
      this.drawRectangle(...) // revert to original
      this.canvas.canvas.style.cursor = 'default';
    }
  }

  onMouseDrag(e) {
    if( // mouse is above of the shape position
      e.clientX >= this.x &&
      e.clientX <= this.x + this.width &&
      e.clientY >= this.y &&
      e.clientY <= this.y + this.height &&
      !this.isDragged
    ) {

      this.canvas.canvas.onmousemove = (drag) => {  // while dragging
        this.eraseRectangle(...); // erase the rectangle

        this.drawRectangle(...); // re-draw the rectangle while dragging

        this.x = drag.clientX; // set the new x-axis value
        this.y = drag.clientY; // set the new y-axis value
      }
      this.isDragged = true;
    }
  }

  onMouseRelease(e) {
    if(this.isDragged) {
      this.isDragged = false;
      this.canvas.canvas.onmousemove = (e) => { // reset it to the original
        this.onMouseHover(e);
      }
    }
  }

  drawRectangle(width, height, x, y, fillStyle, strokeStyle) {
    this.context.beginPath();
    this.context.fillStyle = fillStyle;
    this.context.strokeStyle = strokeStyle;
    this.context.rect(x, y, width, height);
    this.context.fill();
    this.context.stroke();
  }

  eraseRectangle(width, height, x, y) {
    this.context.beginPath();
    this.context.clearRect(x-1, y-1, width+2, height+2);
  }
}

在主类中。

class Main {

  static ready() {

    // Basic canvas options
    const options = {
      width: window.innerWidth,
      height: window.innerHeight,
      background: 'black'
    }

    // Create the canvas object
    const canvas = HTMLCanvas.build('workspace', options);
    const context = canvas.context2D;

    const rect1 = Rectangle.draw({
      width: 200,
      height: 100,
      x: 100,
      y: 200,
      border: 'blue',
      background: 'white',
      label: 'rect1'
    }, canvas);

    const rect2 = Rectangle.draw({
      width: 300,
      height: 75,
      x: 200,
      y: 400,
      border: 'green',
      background: 'white',
      label: 'rect2'
    }, canvas);
  }
 }

// Initialize the object
Main.ready();

使用上面的代码,我可以在画布上渲染一个矩形。但是,问题是一旦对象rect2 被拖动,我就无法再拖动对象rect1

rect1 丢失其引用

我无法拖动或悬停rect1 对象,但我仍然可以悬停并拖动/释放rect2 对象。此外,当rect2 被拖到rect1 上时,rect2 会擦除rect1

示例输出

我们如何跟踪每个 HTML5 画布对象的鼠标事件?大多数解决方案最终会迭代所有对象(将在画布中绘制)并将鼠标事件绑定到它。我希望它在对象本身中,而不是在工厂类中。

【问题讨论】:

  • 鼠标输入问题的简单解决方案是为上下文本身设置一个处理程序,如果鼠标与其相交,它将手动调用矩形的处理程序之一

标签: javascript html canvas


【解决方案1】:

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<style>
			body {
				background-color: black;
			}
			
			canvas {
				position: absolute;
				margin-left: auto;
				margin-right: auto;
				left: 0;
				right: 0;
				border: solid 1px white;
				border-radius: 10px;
			}
		</style>
	</head>
	
	<body>
		<canvas id="canvas"></canvas>
		<script type="application/javascript">
			
			var imageWidth = 180;
			var imageHeight = 160;
			var canvas = null;
			var ctx = null;
			var bounds = null;
			var selectedBox = null;
			var panX = 0;
			var panY = 0;
			var mouseX = 0;
			var mouseY = 0;
			var oldMouseX = 0;
			var oldMouseY = 0;
			var mouseHeld = false;
			var boxArray = [];
			
			function DraggableBox(x,y,width,height,text) {
				this.x = x;
				this.y = y;
				this.width = width;
				this.height = height;
				this.text = text;
				this.isSelected = false;
			}
			
			DraggableBox.prototype.isCollidingWidthPoint = function(x,y) {
				return (x > this.x && x < this.x + this.width)
					&& (y > this.y && y < this.y + this.height);
			}
			
			DraggableBox.prototype.drag = function(newX,newY) {
				this.x = newX - this.width * 0.5;
				this.y = newY - this.height * 0.5;
			}
			
			DraggableBox.prototype.draw = function() {
				if (this.isSelected) {
					ctx.fillStyle = "darkcyan";
					ctx.fillRect(
						this.x - panX,
						this.y - panY,
						this.width,
						this.height
					);
					ctx.fillStyle = "black";
				} else {			
					ctx.fillRect(
						this.x - panX,
						this.y - panY,
						this.width,
						this.height
					);
				}
				
				ctx.fillStyle = "white";
				ctx.fillText(
					this.text,
					this.x + this.width * 0.5 - panX,
					this.y + this.height * 0.5 - panY,
					this.width
				);
				ctx.fillStyle = "black";
			}
			
			window.onmousedown = function(e) {
				mouseHeld = true;
			
				if (!selectedBox) {
					for (var i = boxArray.length - 1; i > -1; --i) {
						if (boxArray[i].isCollidingWidthPoint(mouseX + panX,mouseY + panY)) {
							selectedBox = boxArray[i];
							selectedBox.isSelected = true;
							requestAnimationFrame(draw);
							return;
						}
					}
				}
			}
			
			window.onmousemove = function(e) {
				mouseX = e.clientX - bounds.left;
				mouseY = e.clientY - bounds.top;
				
				if (mouseHeld) {
					if (!selectedBox) {
						panX += oldMouseX - mouseX;
						panY += oldMouseY - mouseY;
					} else {
						selectedBox.x = mouseX - selectedBox.width * 0.5 + panX;
						selectedBox.y = mouseY - selectedBox.height * 0.5 + panY;
					}
				}
				
				oldMouseX = mouseX;
				oldMouseY = mouseY;
				
				requestAnimationFrame(draw);
			}
			
			window.onmouseup = function(e) {
				mouseHeld = false;
				
				if (selectedBox) {
					selectedBox.isSelected = false;
					selectedBox = null;
					requestAnimationFrame(draw);
				}
			}
			
			function draw() {
				ctx.fillStyle = "gray";
				ctx.fillRect(0,0,imageWidth,imageHeight);
				
				var box = null;
				var xMin = 0;
				var xMax = 0;
				var yMin = 0;
				var yMax = 0;
				
				ctx.fillStyle = "black";
				
				for (var i = 0; i < boxArray.length; ++i) {
					box = boxArray[i];
					
					xMin = box.x - panX;
					xMax = box.x + box.width - panX;
					yMin = box.y - panY;
					yMax = box.y + box.height - panY;
					
					if (xMax > 0 && xMin < imageWidth && yMax > 0 && yMin < imageHeight) {
						box.draw();
					}
				}
			}
			
			window.onload = function() {
				canvas = document.getElementById("canvas");
				canvas.width = imageWidth;
				canvas.height = imageHeight;
				
				bounds = canvas.getBoundingClientRect();
				ctx = canvas.getContext("2d");
				ctx.textAlign = "center";
				ctx.font = "15px Arial"
				
				boxArray.push(new DraggableBox(Math.random() * 320,Math.random() * 240,100,25,"This is a draggable text box"));
				boxArray.push(new DraggableBox(Math.random() * 320,Math.random() * 240,100,50,"Another text box"));
				boxArray.push(new DraggableBox(Math.random() * 320,Math.random() * 240,100,50,"Text in a box"));
				boxArray.push(new DraggableBox(Math.random() * 320,Math.random() * 240,100,50,"I find this box quite texing"));
				boxArray.push(new DraggableBox(Math.random() * 320,Math.random() * 240,150,50,"You weren't supposed to find this box"));
				requestAnimationFrame(draw);
			}
			
			window.onunload = function() {
				canvas = null;
				ctx = null;
				bounds = null;
				selectedBox = null;
				boxArray = null;
			}
			
		</script>
	</body>
</html>

【讨论】:

  • 您能提供一些解释吗?当你只发布代码时,很难理解你做了什么。
  • 实际上您的代码并没有丢失对画布的引用,发生的事情是您正在覆盖画布的事件处理程序。
  • 代替this.canvas.onmouse,使用this.canvas.addEventListener("move",function(e) {});这使您可以为同一事件拥有多个事件处理程序。
猜你喜欢
  • 1970-01-01
  • 2013-12-17
  • 1970-01-01
  • 2017-08-08
  • 2011-02-10
  • 2014-02-16
  • 2021-08-29
  • 2014-09-17
  • 1970-01-01
相关资源
最近更新 更多