Home > Software engineering >  HTML javascript, dragging method in canvas
HTML javascript, dragging method in canvas

Time:12-28

so i made a bunch of squares grids using html canvas. I am implementing the drag mechanism, and i only want to allow user to drag in rectangle shape. right now, depending how user drag, they can make an another shape as shown in below. And this is a side questions, i am making a very large grid using html canvas and its little laggy, any tips for that ? enter image description here

Here are my code of html and javascript

function getSquare(canvas, evt) {

    var rect = canvas.getBoundingClientRect();
    return {
        x: 1   (evt.clientX - rect.left) - (evt.clientX - rect.left),
        y: 1   (evt.clientY - rect.top) - (evt.clientY - rect.top)
    };
}


function emptySquare(context) {
    context.rect(0, 0, canvas.width, canvas.height);
    context.fillStyle = "#ffffff"
    context.fill();
    context.strokeStyle = "#ddd";
    context.stroke();
}
function range(start, end) {
    var ans = [];
    if (end > start) {
        for (let i = start; i <= end; i  = 10) {
            ans.push(i);
        }
    } else {
        emptySquare(context);
        for (let i = start; i >= end; i -= 10) {
            ans.push(i);
    }}
    return ans;
}
function drawBoard(context) {
    for (var x = 0.5; x < 20001; x  = 10) {
      context.moveTo(x, 0);
      context.lineTo(x,20000);
    }

    for (var y = 0.5; y < 20001; y  = 10) {
      context.moveTo(0, y);
      context.lineTo(20000, y);
    }

    context.strokeStyle = "#ddd";
    context.stroke();
}

function fillSquare(context, x, y){
    context.fillStyle = "#70B7B5";
    context.fillRect(x,y,9,9);
}

var canvas = document.getElementById('myBoard');
var context = canvas.getContext('2d');
var A;
drawBoard(context);
var isDrag= false;
// drawBoard(context);


canvas.addEventListener('mousedown', function(evt) {
    var mousePos = getSquare(canvas, evt);
    isDrag=true;
    fillSquare(context, mousePos.x, mousePos.y);
    
    previousPos = mousePos;


}, false);

 
canvas.addEventListener('mousemove', function(evt) {
    if (isDrag) {
        
        var mousePos = getSquare(canvas, evt);
        var x_dist = range(previousPos.x, mousePos.x);
        var y_dist = range(previousPos.y, mousePos.y);
        
        for (x in x_dist) {
            for (y in y_dist) {
                fillSquare(context, x_dist[x], y_dist[y]);
                }
            }
    }
}, false);

canvas.addEventListener('mouseup', function(evt) {
    if (isDrag){
        isDrag = false;
    } 

}, false);

// const p = context.lineWidth / 2; //padding
//       const x =  30;
//       const y =  30;
//       const img = new Image();
//       img.src = `snsd.jpg`;
      // img.onload = function() {
      //   A = context.drawImage(img, 0, 0,200,200);
      //   context.strokeRect(0, 0, 200, 200);
      // };

      // img2 = new Image();
      // img2.src = `ive.jpg`;
      // img2.onload = function() {
      //   A = context.drawImage(img2, 200, 200,200,200);
      // };

      // img3 = new Image();
      // img3.src = `twice.jpg`;
      // img3.onload = function() {
      //   A = context.drawImage(img3, 0, 200,200,200);
      // };

      // img4 = new Image();
      // img4.src = `blackpink.jpg`;
      // img4.onload = function() {
      //   A = context.drawImage(img4, 200, 0,200,200);
      // };
      // img5 = new Image();
      // img5.src = `promis9.jpg`;
      // img5.onload = function() {
      //   A = context.drawImage(img5, 400, 0,400,400);
      // };



  // for (let xCell = 0; xCell < 10; xCell  ) {
  //   for (let yCell = 0; yCell < 10; yCell  ) {
  //     const x = xCell * 30;
  //     const y = yCell * 30;
  //     const img = new Image();
  //     img.onload = function() {
  //       context.drawImage(img, x p, y p, 60-p*2, 60-p*2);
  //     };
  //     img.src = `https://dummyimage.com/60x60/000/fff&text=${xCell},${yCell}`;
  //   }
  // }
var canvas = document.getElementById('myBoard');
var context = canvas.getContext('2d');

// drawBoard(context);

var isDrag=false;

canvas.addEventListener('mousedown', function(evt) {
    var mousePos = getSquare(canvas, evt);
    isDrag=true;
    fillSquare(context, mousePos.x, mousePos.y);
    
    previousPos = mousePos;


}, false);

 
canvas.addEventListener('mousemove', function(evt) {
    if (isDrag) {
        
        var mousePos = getSquare(canvas, evt);
        var x_dist = range(previousPos.x, mousePos.x);
        var y_dist = range(previousPos.y, mousePos.y);
        
        for (x in x_dist) {
            for (y in y_dist) {
                fillSquare(context, x_dist[x], y_dist[y]);
                }
            }
    }
}, false);

canvas.addEventListener('mouseup', function(evt) {
    if (isDrag){
        isDrag = false;
    } 

}, false);
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
  <link rel="stylesheet" href="./assets/css/style.css">


    
    <meta charset="utf-8">
</head>

<body>


<!-- <div class='container'>
<nav >
  <div >
    <a  href="#">Navbar</a>
    <button  type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
      <span ></span>
    </button>
    <div  id="navbarNavAltMarkup">
      <div >
        <a  aria-current="page" href="#">Home</a>
        <a  href="#">Features</a>
        <a  href="#">Pricing</a>
        <a  href="#" tabindex="-1" aria-disabled="true">Disabled</a>
      </div>
    </div>
  </div>
 -->  
 <nav >
  <div >
      <a  href="index.html">t</a>
      <button  type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
        <span ></span>
      </button>

      <div  id="navbarNavAltMarkup">
        <div >
            <a  aria-current="page" href="index.html">Home</a>
            <a  href="#">About Us</a>
            <a  href="Myblock.html">Mk</a>
        </div>
        <div >
          <a  href="login.html">Login</a>
        </div>
      </div>
   </div>
  </nav>
<div>

            <div  id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
          <div >
            <div >
              <div >
                <h5  id="exampleModalLabel">간판 구입</h5>
                <button type="button"  data-bs-dismiss="modal" aria-label="Close"></button>
              </div>
              <div >
                    <form action="" method="POST" enctype="multipart/form-data">
                      <div >
                        <div >
                          <div >
                            <div >
                         
                              <div >
                                <div >
                                  <div >
                                    <div><b>미리보기</b></div>
  <!--                                   <div >
                                      <button type="button" >
                                        <i ></i> 초기화
                                      </button>
                                    </div> -->
                                  </div>
                                  <div ></div>
                                </div>
                              </div>
                              <div >
                                <div >
                                  <i ></i>
                                  <p>광고 이미지 선택 or 드래그해 옮겨 오세요.</p>
                                </div>
                                <input type="file" name="img_logo" >
                              </div>
                            </div>
                          </div>
                        </div>
                      </div>
                        <div >
                            <label for="exampleInputEmail1">닉네임/이름</label>
                            <input type="email"  id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="닉네임/이름">
                            <label for="exampleInputEmail1">연락처</label>
                            <input type="email"  id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="핸드폰 번호">
                            <label for="exampleInputEmail1">구매희망 면적</label>
                            <input type="email"  id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="ex) 10 x 10 OR 100x100 ">
                        </div>
                    </form>
                

              </div>
              <div >
                <button type="button"  data-bs-dismiss="modal">닫기</button>
                <button type="submit" >적용하기</button>
              </div>
            </div>
          </div>
        </div>
        <div>
        <canvas id="myBoard" width="1440" height="1200"></canvas>
      </div>


  </hr>

  <footer>
          <p>© 2021 Nune Project</p>
  </footer>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
    <script type="text/javascript" src="./assets/js/mainBoard.js"></script>
    <script type="text/javascript" src="./assets/js/drag_field.js"></script>
</body>

CodePudding user response:

The issue is being seen because range function is not clearing the squares when end > start. Clearing the squares in both the cases resolves the issue.

Here's the snippet that fixes the issue.

function range(start, end) {
  var ans = [];
  emptySquare(context);
  if (end > start) {
    for (let i = start; i <= end; i  = 10) {
        ans.push(i);
    }
  } else {
    for (let i = start; i >= end; i -= 10) {
        ans.push(i);
    }
  }
  return ans;
} 

The Lag Issue

The callback function attached to mousemove clears and draws squares on every invocation - a time consuming operation. Given that JavaScript is single threaded and this function is called at a very high rate, it might be the cause of the lag.

Debouncing the mousemove callback can help reducing the lag.

CodePudding user response:

Another approach to solve the lag issue is to make two canvases: one for the background grid lines and the other one for drawing the green rectangles.

<div>
  <canvas id="bg" width="1440" height="1200"></canvas>
  <canvas id="myBoard" width="1440" height="1200"></canvas>
</div>

Set the #bg canvas so that #myBoard is in front of it:

#bg {
  position: absolute;
  z-index: -1;
}

Then draw the grid lines on the #bg canvas:

const bg = document.getElementById('bg')
const bgContext = bg.getContext('2d')
drawBoard(bgContext)

Finally, move the emptySquare(context) function into the mousemove event callback and edit it as follows:

function emptySquare(context) {
  context.clearRect(0, 0, canvas.width, canvas.height)
}

So it will only clear the #myBoard canvas while #bg stays the same without needing to redraw it each time.

Example:

const bg = document.getElementById('bg')
const bgContext = bg.getContext('2d')
drawBoard(bgContext)



function getSquare(canvas, evt) {
  var rect = canvas.getBoundingClientRect()
  return {
    x: 1   (evt.clientX - rect.left) - ((evt.clientX - rect.left) % 10),
    y: 1   (evt.clientY - rect.top) - ((evt.clientY - rect.top) % 10),
  }
}

function emptySquare(context) {
  context.clearRect(0, 0, canvas.width, canvas.height)
}

function range(start, end) {
  var ans = []
  if (end > start) {
    for (let i = start; i <= end; i  = 10) {
      ans.push(i)
    }
  } else {

    for (let i = start; i >= end; i -= 10) {
      ans.push(i)
    }
  }
  return ans
}

function drawBoard(context) {
  for (var x = 0.5; x < 20001; x  = 10) {
    context.moveTo(x, 0)
    context.lineTo(x, 20000)
  }

  for (var y = 0.5; y < 20001; y  = 10) {
    context.moveTo(0, y)
    context.lineTo(20000, y)
  }

  context.strokeStyle = '#ddd'
  context.stroke()
}

function fillSquare(context, x, y) {
  context.fillStyle = '#70B7B5'
  context.fillRect(x, y, 9, 9)
}

var canvas = document.getElementById('myBoard')
var context = canvas.getContext('2d')
var isDrag = false

canvas.addEventListener('mousedown', function(evt) {
  var mousePos = getSquare(canvas, evt)
  isDrag = true
  fillSquare(context, mousePos.x, mousePos.y)
  previousPos = mousePos
}, false)

canvas.addEventListener('mousemove', function(evt) {
  if (isDrag) {
    var mousePos = getSquare(canvas, evt)
    var x_dist = range(previousPos.x, mousePos.x)
    var y_dist = range(previousPos.y, mousePos.y)

    emptySquare(context)

    for (x in x_dist) {
      for (y in y_dist) {
        fillSquare(context, x_dist[x], y_dist[y])
      }
    }
  }
}, false)

canvas.addEventListener('mouseup', function(evt) {
  if (isDrag) {
    isDrag = false
  }
}, false)
#bg {
  position: absolute;
  z-index: -1;
}
<div>
  <canvas id="bg" width="1440" height="1200"></canvas>
  <canvas id="myBoard" width="1440" height="1200"></canvas>
</div>

  • Related