HTML 5 Canvas拖放多个对象不起作用 [英] HTML 5 Canvas Drag and Drop upon multiple objects not working
问题描述
我有自定义代码在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
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屋!