嗨,我正在尝试制作一个绘画应用程序,在其中可以绘制直线和矩形,但是我遇到了问题
第一次按原样绘制矩形或直线,但随后绘制的是带有对角线的矩形。
我不知道为什么第一次绘制好直线或矩形然后再绘制带有对角线的矩形。
var storedLines = [];
var startX = 0;
var startY = 0;
var isDown;
var type = "line"; // current type
class CanvasState{
// **** First some setup! ****
constructor(canvas){
this.canvas = canvas;
this.type = type;
this.width = canvas.width;
this.height = canvas.height;
this.ctx = canvas.getContext('2d');
var offsetX = canvas.offsetLeft;
var offsetY = canvas.offsetTop;
this.valid = false; // when set to false, the canvas will redraw everything
this.shapes = []; // the collection of things to be drawn
this.dragging = false; // Keep track of when we are dragging
this.selection = null;
this.dragoffx = 0; // See mousedown and mousemove events for explanation
this.dragoffy = 0;
var myState = this;
this.selectionColor = '#CC0000';
this.selectionWidth = 2;
canvas.addEventListener('selectstart', function(e) { e.preventDefault(); return false; }, false);
// Up, down, and move are for dragging
canvas.addEventListener('mousedown', function(e) {
e.preventDefault();
e.stopPropagation();
canvas.style.cursor = "crosshair";
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
isDown = true;
startX = mouseX;
startY = mouseY;
}, true);
canvas.addEventListener('mousemove', function(e) {
e.preventDefault();
e.stopPropagation();
var ctx = canvas.getContext('2d');
if (!isDown) return;
myState.redrawStoredLines();
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
if(type == "rect"){
ctx.beginPath();
ctx.rect(startX, startY, mouseX - startX, mouseY - startY);
ctx.stroke();
}
if(type == "line"){
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(mouseX, mouseY);
ctx.stroke();
}
}, true);
canvas.addEventListener('mouseup', function(e) {
canvas.style.cursor = "default";
e.preventDefault();
e.stopPropagation();
isDown = false;
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
storedLines.push({
type: type,
x1: startX,
y1: startY,
x2: mouseX,
y2: mouseY
});
console.log(storedLines);
myState.redrawStoredLines();
}, true);
canvas.addEventListener('handleMouseOut', function(e) {
e.preventDefault();
e.stopPropagation();
if (!isDown) return;
isDown = false;
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
storedLines.push({
type: type,
x1: startX,
y1: startY,
x2: mouseX,
y2: mouseY
});
myState.redrawStoredLines();
}, true);
}
setType(newtype){
if ( newtype === 'line' ) {
this.type = "line";
}
if ( newtype === 'rect' ) {
this.type = "rect";
console.log('settype:' + this.type);
}
}
redrawStoredLines() {
var ctx = this.canvas.getContext('2d');
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
if (storedLines.length == 0) return;
// redraw each stored line
for (var i = 0; i < storedLines.length; i++) {
if(storedLines[i].type === "line"){
ctx.beginPath();
ctx.moveTo(storedLines[i].x1, storedLines[i].y1);
ctx.lineTo(storedLines[i].x2, storedLines[i].y2);
ctx.stroke();
}
if(storedLines[i].type === "rect"){
ctx.beginPath();
ctx.rect(storedLines[i].x1, storedLines[i].y1,
storedLines[i].x2 - storedLines[i].x1, storedLines[i].y2 - storedLines[i].y1);
ctx.stroke();
}
}
}
}
var radios = document.querySelectorAll('input[type=radio][name="shape"]');
function changeHandler(event) {
console.log(event.srcElement.value);
mycanvas.setType(event.srcElement.value);
}
Array.prototype.forEach.call(radios, function(radio) {
radio.addEventListener('change', changeHandler);
});
var mycanvas = new CanvasState(document.getElementById('mainCanvas'));
body{
font-family: Arial, Helvetica, sans-serif;
font-weight: 16px;
}
.container{
margin: 10px;
padding: 2px;
border: solid 1px black;
background-color: #505050
}
h3{
margin: 10px;
padding: 2px;
}
.toolbox{
display: inline;
list-style-type: none;
}
.toolbox > li{
display: inline-block;
margin: 0;
vertical-align: middle;
}
div.sliders div{
/*margin-top: 20px; */
display: inline-block;
}
.in-line p{
font-size: 14px;
display: block;
}
.in-line input{
margin: 5px;
display: block;
}
#square{
margin-top: 20px;
margin-left: 48%;
width: 50px;
height: 50px;
background-color: rgb(127, 127, 127);
}
#rgbcolor{
font-size: 14px;
text-align: center;
}
#clearbtn{
width: 50px;
margin-left: 48%;
margin-top: 10px;
}
.canvas{
margin: 10px;
position: relative;
width: 600px;
height: 400px;
border: solid 1px black;
background-color: #ffffff;
cursor: crosshair;
}
#coords{
text-align: center;
}
<div class="container">
<h3>Drawing Shapes</h3>
<div>
<ul class="toolbox">
<li id="btns">
<div>
<input type="radio" name="shape" id="line" value="line">Line<br>
<input type="radio" name="shape" id="rect" value="rect">Rectangle<br>
</div>
<li id="cnvs">
<div>
<canvas id="mainCanvas" class="canvas" width="600" height="400"></canvas>
<h3 id="coords">(X, Y) : (0 , 0)</h3>
</div>
</li>
</ul>
</div>
</div>
答案 0 :(得分:1)
因此,您的问题出在处理type
选择的更改上。
var p = new CanvasState(document.getElementById('mainCanvas'),type);
每次更改选择时,都会创建一个新的CanvasState
,并在其中添加EventListeners。事实是,您永远不会删除那些EventListener,也不会明确删除以前的CanvasState
。
因此,当您绘制第一条线或矩形时,一切正常,因为只有一个CanvasState
。但是,一旦选择其他类型,就会创建第二个CanvasState
,它将使用新类型。
您可以通过在CanvasState中创建一个setType(newType)
方法并在changeHandler
函数中调用该方法来轻松解决此问题。
然后,您必须在该函数之外创建CanvasState
并保留对其的引用,以便可以调用myCanvas.setType(newType)
。
编辑:
更“现代”的解决方案是从addEventListener
调用中提取handle函数,方法如下:
handleMouseUp(event, type) {
// Here your code for mouseUp
// Edit the references to use the fields from "this"
}
然后您可以像这样添加它们:
canva.addEventListener("mouseup", (e) => this.handleMouseUp(e, this.type), true);
通过这种方式,您不仅限于“ this”的eventListener / canvas环境,还可以访问为CanvasState
设置的所有字段。
答案 1 :(得分:1)
我不想相信sylvain上面给出的答案。这恰恰是问题的根本原因。我已经赞成他的回答。 第二个问题是全局变量类型。在事件处理程序内部,您使用的是全局变量类型,默认情况下将其声明为line。您的setType方法正在设置CanvasState的类型。因此,我进行了相应的更改。您可以在下面参考它们。与此相关的另一个问题是,如果您仍然不选择单选按钮,就可以画线。
如果您想做更多的实验,可以使用removeEventListner显式删除事件处理程序,这将是一个有趣的练习。
另一个选择是,您可以通过克隆div mainCanvas而不替换其事件处理程序来替换它。
clone = div.cloneNode(false);// setting false will not clone child element and event handlers.
但是,这可能会在绘制新形状时增加延迟。
var storedLines = [];
var startX = 0;
var startY = 0;
var isDown;
var type = "line"; // current type
class CanvasState{
// **** First some setup! ****
constructor(canvas){
this.canvas = canvas;
this.type = type;
this.width = canvas.width;
this.height = canvas.height;
this.ctx = canvas.getContext('2d');
var offsetX = canvas.offsetLeft;
var offsetY = canvas.offsetTop;
this.valid = false; // when set to false, the canvas will redraw everything
this.shapes = []; // the collection of things to be drawn
this.dragging = false; // Keep track of when we are dragging
this.selection = null;
this.dragoffx = 0; // See mousedown and mousemove events for explanation
this.dragoffy = 0;
var myState = this;
this.selectionColor = '#CC0000';
this.selectionWidth = 2;
var that = this;
canvas.addEventListener('selectstart', function(e) { e.preventDefault(); return false; }, false);
// Up, down, and move are for dragging
canvas.addEventListener('mousedown', function(e) {
e.preventDefault();
e.stopPropagation();
canvas.style.cursor = "crosshair";
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
isDown = true;
startX = mouseX;
startY = mouseY;
}, true);
canvas.addEventListener('mousemove', function(e) {
e.preventDefault();
e.stopPropagation();
var ctx = canvas.getContext('2d');
if (!isDown) return;
myState.redrawStoredLines();
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
if(that.type == "rect"){
ctx.beginPath();
ctx.rect(startX, startY, mouseX - startX, mouseY - startY);
ctx.stroke();
}
if(that.type == "line"){
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(mouseX, mouseY);
ctx.stroke();
}
}, true);
canvas.addEventListener('mouseup', function(e) {
canvas.style.cursor = "default";
e.preventDefault();
e.stopPropagation();
isDown = false;
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
storedLines.push({
type: that.type,
x1: startX,
y1: startY,
x2: mouseX,
y2: mouseY
});
console.log(storedLines);
myState.redrawStoredLines();
}, true);
canvas.addEventListener('handleMouseOut', function(e) {
e.preventDefault();
e.stopPropagation();
if (!isDown) return;
isDown = false;
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
storedLines.push({
type: that.type,
x1: startX,
y1: startY,
x2: mouseX,
y2: mouseY
});
myState.redrawStoredLines();
}, true);
}
setType(newtype){
if ( newtype === 'line' ) {
this.type = "line";
}
if ( newtype === 'rect' ) {
this.type = "rect";
console.log('settype:' + this.type);
}
}
redrawStoredLines() {
var ctx = this.canvas.getContext('2d');
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
if (storedLines.length == 0) return;
// redraw each stored line
for (var i = 0; i < storedLines.length; i++) {
if(storedLines[i].type === "line"){
ctx.beginPath();
ctx.moveTo(storedLines[i].x1, storedLines[i].y1);
ctx.lineTo(storedLines[i].x2, storedLines[i].y2);
ctx.stroke();
}
if(storedLines[i].type === "rect"){
ctx.beginPath();
ctx.rect(storedLines[i].x1, storedLines[i].y1,
storedLines[i].x2 - storedLines[i].x1, storedLines[i].y2 - storedLines[i].y1);
ctx.stroke();
}
}
}
}
var radios = document.querySelectorAll('input[type=radio][name="shape"]');
function changeHandler(event) {
console.log(event.srcElement.value);
mycanvas.setType(event.srcElement.value);
}
Array.prototype.forEach.call(radios, function(radio) {
radio.addEventListener('change', changeHandler);
});
var mycanvas = new CanvasState(document.getElementById('mainCanvas'));
body{
font-family: Arial, Helvetica, sans-serif;
font-weight: 16px;
}
.container{
margin: 10px;
padding: 2px;
border: solid 1px black;
background-color: #505050
}
h3{
margin: 10px;
padding: 2px;
}
.toolbox{
display: inline;
list-style-type: none;
}
.toolbox > li{
display: inline-block;
margin: 0;
vertical-align: middle;
}
div.sliders div{
/*margin-top: 20px; */
display: inline-block;
}
.in-line p{
font-size: 14px;
display: block;
}
.in-line input{
margin: 5px;
display: block;
}
#square{
margin-top: 20px;
margin-left: 48%;
width: 50px;
height: 50px;
background-color: rgb(127, 127, 127);
}
#rgbcolor{
font-size: 14px;
text-align: center;
}
#clearbtn{
width: 50px;
margin-left: 48%;
margin-top: 10px;
}
.canvas{
margin: 10px;
position: relative;
width: 600px;
height: 400px;
border: solid 1px black;
background-color: #ffffff;
cursor: crosshair;
}
#coords{
text-align: center;
}
<div class="container">
<h3>Drawing Shapes</h3>
<div>
<ul class="toolbox">
<li id="btns">
<div>
<input type="radio" name="shape" id="line" value="line">Line<br>
<input type="radio" name="shape" id="rect" value="rect">Rectangle<br>
</div>
<li id="cnvs">
<div>
<canvas id="mainCanvas" class="canvas" width="600" height="400"></canvas>
<h3 id="coords">(X, Y) : (0 , 0)</h3>
</div>
</li>
</ul>
</div>
</div>