HTML 5 Canvas拖放多个对象不起作用 [英] HTML 5 Canvas Drag and Drop upon multiple objects not working

查看:96
本文介绍了HTML 5 Canvas拖放多个对象不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有自定义代码在HTML5画布上创建和渲染对象。

I have a custom code to create and render objects on HTML5 canvas.

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 in the above of 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 in the above of 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(...); // draw the rectangle while dragging

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

  onMouseRelease(e) {
    if(this.isDragged) {
      this.isDragged = false;
      this.canvas.canvas.onmousemove = (e) => { // reset it to 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 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 everything
Main.ready();

使用上面的代码,我能够在画布中呈现矩形形状。但是,问题是对象 rect1 正在丢失其引用。

With the code above, I was able to render rectangle shape in the canvas. However, the problem is that object rect1 is losing its references.

rect1 丢失对画布的引用

rect1 lose reference to the canvas

我无法拖动或悬停 rect1 对象,但我仍然可以悬停并拖放 rect2 对象。当 rect2 悬停拖过 rect1 时, rect2 删除 rect1

I cannot drag nor hover the rect1 object but I can still hover and drag/drop the rect2 object. When rect2 is hovering dragged over the rect1, rect2 erases the rect1.

示例输出

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

How we can track mouse event for each HTML5 canvas object? Most solutions would end up to iterate all of the objects (which will be drawn in the canvas) and bind the mouse event to it. I want it to be within the object itself, not within a factory class.

推荐答案

<!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>

这篇关于HTML 5 Canvas拖放多个对象不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆