Salut, j'essaie de créer une application de peinture dans laquelle nous pouvons dessiner une ligne et un rectangle mais j'ai un problème

La première fois le rectangle ou la ligne est tracé comme il se doit mais ensuite c'est un rectangle avec une diagonale qui est tracé.

Je ne peux pas comprendre pourquoi la première fois que la ligne ou le rectangle est bien dessiné, puis qu'un rectangle avec une diagonale est dessiné.

    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>  
2
sylvain 19 mars 2019 à 12:12

2 réponses

Meilleure réponse

Votre problème réside donc dans la façon dont vous gérez le changement de la sélection type.

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

Chaque fois que vous modifiez la sélection, vous créez un nouveau CanvasState dans lequel vous ajoutez les EventListeners. Le fait est que vous ne supprimez jamais ces EventListeners et que vous ne supprimez pas explicitement votre CanvasState précédent.

Ainsi, lorsque vous dessinez vos premières lignes ou rectangles, tout fonctionne bien car il n'y a qu'un seul CanvasState. Mais dès que vous sélectionnez un type différent, un deuxième CanvasState est créé qui utilisera le nouveau type.

Vous pouvez facilement résoudre ce problème en créant une méthode setType(newType) dans votre CanvasState pour l'appeler dans la fonction changeHandler.

Ensuite, vous devez créer votre CanvasState en dehors de cette fonction et conserver une référence pour pouvoir appeler myCanvas.setType(newType).

MODIFIER:

Une solution plus "moderne" consisterait à extraire la fonction handle de vos appels addEventListener en tant que méthode telle que :

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

Et puis vous pouvez les ajouter comme :

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

De cette façon, vous n'êtes pas limité à l'environnement eventListener/canvas de "this" mais pouvez accéder à tous les champs que vous avez définis pour CanvasState.

1
BreakBB 19 mars 2019 à 14:36

Je ne veux pas durcir le crédit de la réponse donnée ci-dessus par sylvain. C'est exactement la cause première du problème. J'ai déjà voté pour sa réponse. Le deuxième problème concerne le type de variable globale. À l'intérieur des gestionnaires d'événements, vous utilisez un type de variable globale qui est déclaré ligne par défaut. Et votre méthode setType définit le type de CanvasState. J'ai donc apporté des modifications en conséquence. Vous pouvez les consulter ci-dessous. Avec cela, un autre problème est que si vous ne sélectionnez pas le bouton radio, vous pourrez toujours tracer la ligne.

Si vous souhaitez expérimenter un peu plus, vous pouvez supprimer explicitement les gestionnaires d'événements en utilisant removeEventListner Ce sera un exercice amusant.

Une autre option consiste à remplacer le div mainCanvas en le clonant sans cloner ses gestionnaires d'événements.

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

Mais cela pourrait ajouter du décalage lors du dessin d'une nouvelle forme.

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>
1
Rahul Raut 19 mars 2019 à 13:50