简体   繁体   中英

Draw diagonal inside rectangle

Hi I'm trying to make a paint app in which we can draw line and rectangle but I have a problem

the first time the rectangle or the line is draw as it should be but then it's a rectangle with a diagonal which is drawn.

I can't figure out why the first time the line or rectangle is drawn well and then a rectangle with a diagonal is drawn.

 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>

So your problem lies in the way you're handeling the change of the type selection.

var p =  new CanvasState(document.getElementById('mainCanvas'),type);

Every time you change the selection you create a new CanvasState in which you add the EventListeners. Thing is that you never remove those EventListeners and you're not explicit deleting your previous CanvasState .

So when you draw your first lines or rectangles everything is working fine because there is just one single CanvasState . But as soon as you select a different type a second CanvasState is created which will use the new type.

You can easily solve this by creating a setType(newType) method in your CanvasState to call it in the changeHandler function.

Then you have to create your CanvasState outside of that function and keep a reference to it so you can call myCanvas.setType(newType) .

EDIT:

A more "modern" solution would be to extract the handle function from within your addEventListener calls as a method like:

handleMouseUp(event, type) {
  // Here your code for mouseUp
  // Edit the references to use the fields from "this"
}

And then you can add them like:

canva.addEventListener("mouseup", (e) => this.handleMouseUp(e, this.type), true);

That way you're not limited to the eventListener/canvas environment of "this" but can access all the fields you set for CanvasState .

I don't want to steel the credit of answer given above by sylvain . That is exactly the root cause of the issue. I have already upvoted for his answer. The second problem is with the global variable type. Inside event-handlers you are using global variable type which is declared line by default. And your setType method is setting the CanvasState's type. So I have made changes accordingly. You can refer them below. With this another issue is if you don't select the radio button still you will be able to draw the line.

If you want to experiment little bit more you can explicitly remove event handlers using removeEventListner This will be fun exercise.

One more option is you can replace the div mainCanvas by cloning it without cloning it's event handlers.

clone = div.cloneNode(false);// setting false will not clone child element and event handlers. 

But that might add lag while drawing a new shape.

 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM