Creating a draggable and scaleable grid in HTML5












1















Unlike the other HTML5 how to create a grid questions, I'm wondering how to make one draggable and scalable.



Drawing the grid is pretty simple:






var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
var width = window.innerWidth;
var height = window.innerHeight;

c.width = width;
c.height = height;

drawGrid(width, height, 40);

function drawGrid(gridWidth, gridHeight, boxSize) {
ctx.clearRect(0, 0, c.width, c.height);
ctx.beginPath();
for (var i = 0; i <= gridWidth; i += boxSize) {
ctx.moveTo(i, 0);
ctx.lineTo(i, gridHeight);
}
for (var i = 0; i <= gridHeight; i += boxSize) {
ctx.moveTo(0, i);
ctx.lineTo(gridWidth, i);
}
ctx.strokeStyle = "rgba( 210, 210, 210, 1 )";
ctx.stroke();
}

html,
body {
overflow: hidden;
}

#canvas {
position: absolute;
top: 0;
left: 0;
}

<canvas id="canvas"></canvas>





Now to make it draggable, there are many ways of doing it, but I focused on creating the illusion of an infinite moving grid similar to this:



Example Image
(Sorry, don't have enough credits yet)



As the graph moves toward the right, the lines that are hidden from the canvas size are moved back to the beginning, and vice versa. I'm not too sure how to go about moving the grid with the mouse, as well as the scaling. The lines tend to blur when scaled unlike SVGs.
What's the quickest way of creating a grid to move around infinitely and scale?



EDIT:
I took a similar approach to move the grid around using an image pattern to fill the screen.






var c = document.getElementById("canvas"),
ctx = c.getContext("2d");
var width = window.innerWidth;
var height = window.innerHeight;
var itemIsSelected = false;
var clicked = function(e) {
var x = e.pageX;
var y = e.pageY;
}

draw(width, height);
draggable('#app');

function draw(width, height) {
c.width = width;
c.height = height;
generateBackground();
}

function draggable(item) {
var isMouseDown = false;
document.onmousedown = function(e) {
e.preventDefault();
clicked.x = e.pageX;
clicked.y = e.pageY;
$(item).css('cursor', 'all-scroll');
isMouseDown = true;
};
document.onmouseup = function(e) {
e.preventDefault();
isMouseDown = false;
$(item).css('cursor', 'default');
};
document.onmousemove = function(e) {
e.preventDefault();
if (isMouseDown == true) {
var mouseX = e.pageX;
var mouseY = e.pageY;
generateBackground(mouseX, mouseY, clicked.x, clicked.y);
}
};
}

function generateBackground(x, y, initX, initY) {
distanceX = x - initX;
distanceY = y - initY;
ctx.clearRect(0, 0, c.width, c.height);
var bgImage = document.getElementById("bg")
var pattern = ctx.createPattern(bgImage, "repeat");
ctx.rect(0, 0, width, height);
ctx.fillStyle = pattern;
ctx.fill();
ctx.translate(Math.sqrt(distanceX), Math.sqrt(distanceY));
}

html,
body {
overflow: hidden;
}

#canvas {
top: 0;
left: 0;
position: absolute;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
<img src="https://i.imgur.com/2MupHjw.png" id="bg" hidden>





This method does not allow me to scroll left or up. It also speeds up the dragging after a distance and does not deal with negative motion well.










share|improve this question





























    1















    Unlike the other HTML5 how to create a grid questions, I'm wondering how to make one draggable and scalable.



    Drawing the grid is pretty simple:






    var c = document.getElementById('canvas');
    var ctx = c.getContext('2d');
    var width = window.innerWidth;
    var height = window.innerHeight;

    c.width = width;
    c.height = height;

    drawGrid(width, height, 40);

    function drawGrid(gridWidth, gridHeight, boxSize) {
    ctx.clearRect(0, 0, c.width, c.height);
    ctx.beginPath();
    for (var i = 0; i <= gridWidth; i += boxSize) {
    ctx.moveTo(i, 0);
    ctx.lineTo(i, gridHeight);
    }
    for (var i = 0; i <= gridHeight; i += boxSize) {
    ctx.moveTo(0, i);
    ctx.lineTo(gridWidth, i);
    }
    ctx.strokeStyle = "rgba( 210, 210, 210, 1 )";
    ctx.stroke();
    }

    html,
    body {
    overflow: hidden;
    }

    #canvas {
    position: absolute;
    top: 0;
    left: 0;
    }

    <canvas id="canvas"></canvas>





    Now to make it draggable, there are many ways of doing it, but I focused on creating the illusion of an infinite moving grid similar to this:



    Example Image
    (Sorry, don't have enough credits yet)



    As the graph moves toward the right, the lines that are hidden from the canvas size are moved back to the beginning, and vice versa. I'm not too sure how to go about moving the grid with the mouse, as well as the scaling. The lines tend to blur when scaled unlike SVGs.
    What's the quickest way of creating a grid to move around infinitely and scale?



    EDIT:
    I took a similar approach to move the grid around using an image pattern to fill the screen.






    var c = document.getElementById("canvas"),
    ctx = c.getContext("2d");
    var width = window.innerWidth;
    var height = window.innerHeight;
    var itemIsSelected = false;
    var clicked = function(e) {
    var x = e.pageX;
    var y = e.pageY;
    }

    draw(width, height);
    draggable('#app');

    function draw(width, height) {
    c.width = width;
    c.height = height;
    generateBackground();
    }

    function draggable(item) {
    var isMouseDown = false;
    document.onmousedown = function(e) {
    e.preventDefault();
    clicked.x = e.pageX;
    clicked.y = e.pageY;
    $(item).css('cursor', 'all-scroll');
    isMouseDown = true;
    };
    document.onmouseup = function(e) {
    e.preventDefault();
    isMouseDown = false;
    $(item).css('cursor', 'default');
    };
    document.onmousemove = function(e) {
    e.preventDefault();
    if (isMouseDown == true) {
    var mouseX = e.pageX;
    var mouseY = e.pageY;
    generateBackground(mouseX, mouseY, clicked.x, clicked.y);
    }
    };
    }

    function generateBackground(x, y, initX, initY) {
    distanceX = x - initX;
    distanceY = y - initY;
    ctx.clearRect(0, 0, c.width, c.height);
    var bgImage = document.getElementById("bg")
    var pattern = ctx.createPattern(bgImage, "repeat");
    ctx.rect(0, 0, width, height);
    ctx.fillStyle = pattern;
    ctx.fill();
    ctx.translate(Math.sqrt(distanceX), Math.sqrt(distanceY));
    }

    html,
    body {
    overflow: hidden;
    }

    #canvas {
    top: 0;
    left: 0;
    position: absolute;
    }

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <canvas id="canvas"></canvas>
    <img src="https://i.imgur.com/2MupHjw.png" id="bg" hidden>





    This method does not allow me to scroll left or up. It also speeds up the dragging after a distance and does not deal with negative motion well.










    share|improve this question



























      1












      1








      1


      3






      Unlike the other HTML5 how to create a grid questions, I'm wondering how to make one draggable and scalable.



      Drawing the grid is pretty simple:






      var c = document.getElementById('canvas');
      var ctx = c.getContext('2d');
      var width = window.innerWidth;
      var height = window.innerHeight;

      c.width = width;
      c.height = height;

      drawGrid(width, height, 40);

      function drawGrid(gridWidth, gridHeight, boxSize) {
      ctx.clearRect(0, 0, c.width, c.height);
      ctx.beginPath();
      for (var i = 0; i <= gridWidth; i += boxSize) {
      ctx.moveTo(i, 0);
      ctx.lineTo(i, gridHeight);
      }
      for (var i = 0; i <= gridHeight; i += boxSize) {
      ctx.moveTo(0, i);
      ctx.lineTo(gridWidth, i);
      }
      ctx.strokeStyle = "rgba( 210, 210, 210, 1 )";
      ctx.stroke();
      }

      html,
      body {
      overflow: hidden;
      }

      #canvas {
      position: absolute;
      top: 0;
      left: 0;
      }

      <canvas id="canvas"></canvas>





      Now to make it draggable, there are many ways of doing it, but I focused on creating the illusion of an infinite moving grid similar to this:



      Example Image
      (Sorry, don't have enough credits yet)



      As the graph moves toward the right, the lines that are hidden from the canvas size are moved back to the beginning, and vice versa. I'm not too sure how to go about moving the grid with the mouse, as well as the scaling. The lines tend to blur when scaled unlike SVGs.
      What's the quickest way of creating a grid to move around infinitely and scale?



      EDIT:
      I took a similar approach to move the grid around using an image pattern to fill the screen.






      var c = document.getElementById("canvas"),
      ctx = c.getContext("2d");
      var width = window.innerWidth;
      var height = window.innerHeight;
      var itemIsSelected = false;
      var clicked = function(e) {
      var x = e.pageX;
      var y = e.pageY;
      }

      draw(width, height);
      draggable('#app');

      function draw(width, height) {
      c.width = width;
      c.height = height;
      generateBackground();
      }

      function draggable(item) {
      var isMouseDown = false;
      document.onmousedown = function(e) {
      e.preventDefault();
      clicked.x = e.pageX;
      clicked.y = e.pageY;
      $(item).css('cursor', 'all-scroll');
      isMouseDown = true;
      };
      document.onmouseup = function(e) {
      e.preventDefault();
      isMouseDown = false;
      $(item).css('cursor', 'default');
      };
      document.onmousemove = function(e) {
      e.preventDefault();
      if (isMouseDown == true) {
      var mouseX = e.pageX;
      var mouseY = e.pageY;
      generateBackground(mouseX, mouseY, clicked.x, clicked.y);
      }
      };
      }

      function generateBackground(x, y, initX, initY) {
      distanceX = x - initX;
      distanceY = y - initY;
      ctx.clearRect(0, 0, c.width, c.height);
      var bgImage = document.getElementById("bg")
      var pattern = ctx.createPattern(bgImage, "repeat");
      ctx.rect(0, 0, width, height);
      ctx.fillStyle = pattern;
      ctx.fill();
      ctx.translate(Math.sqrt(distanceX), Math.sqrt(distanceY));
      }

      html,
      body {
      overflow: hidden;
      }

      #canvas {
      top: 0;
      left: 0;
      position: absolute;
      }

      <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
      <canvas id="canvas"></canvas>
      <img src="https://i.imgur.com/2MupHjw.png" id="bg" hidden>





      This method does not allow me to scroll left or up. It also speeds up the dragging after a distance and does not deal with negative motion well.










      share|improve this question
















      Unlike the other HTML5 how to create a grid questions, I'm wondering how to make one draggable and scalable.



      Drawing the grid is pretty simple:






      var c = document.getElementById('canvas');
      var ctx = c.getContext('2d');
      var width = window.innerWidth;
      var height = window.innerHeight;

      c.width = width;
      c.height = height;

      drawGrid(width, height, 40);

      function drawGrid(gridWidth, gridHeight, boxSize) {
      ctx.clearRect(0, 0, c.width, c.height);
      ctx.beginPath();
      for (var i = 0; i <= gridWidth; i += boxSize) {
      ctx.moveTo(i, 0);
      ctx.lineTo(i, gridHeight);
      }
      for (var i = 0; i <= gridHeight; i += boxSize) {
      ctx.moveTo(0, i);
      ctx.lineTo(gridWidth, i);
      }
      ctx.strokeStyle = "rgba( 210, 210, 210, 1 )";
      ctx.stroke();
      }

      html,
      body {
      overflow: hidden;
      }

      #canvas {
      position: absolute;
      top: 0;
      left: 0;
      }

      <canvas id="canvas"></canvas>





      Now to make it draggable, there are many ways of doing it, but I focused on creating the illusion of an infinite moving grid similar to this:



      Example Image
      (Sorry, don't have enough credits yet)



      As the graph moves toward the right, the lines that are hidden from the canvas size are moved back to the beginning, and vice versa. I'm not too sure how to go about moving the grid with the mouse, as well as the scaling. The lines tend to blur when scaled unlike SVGs.
      What's the quickest way of creating a grid to move around infinitely and scale?



      EDIT:
      I took a similar approach to move the grid around using an image pattern to fill the screen.






      var c = document.getElementById("canvas"),
      ctx = c.getContext("2d");
      var width = window.innerWidth;
      var height = window.innerHeight;
      var itemIsSelected = false;
      var clicked = function(e) {
      var x = e.pageX;
      var y = e.pageY;
      }

      draw(width, height);
      draggable('#app');

      function draw(width, height) {
      c.width = width;
      c.height = height;
      generateBackground();
      }

      function draggable(item) {
      var isMouseDown = false;
      document.onmousedown = function(e) {
      e.preventDefault();
      clicked.x = e.pageX;
      clicked.y = e.pageY;
      $(item).css('cursor', 'all-scroll');
      isMouseDown = true;
      };
      document.onmouseup = function(e) {
      e.preventDefault();
      isMouseDown = false;
      $(item).css('cursor', 'default');
      };
      document.onmousemove = function(e) {
      e.preventDefault();
      if (isMouseDown == true) {
      var mouseX = e.pageX;
      var mouseY = e.pageY;
      generateBackground(mouseX, mouseY, clicked.x, clicked.y);
      }
      };
      }

      function generateBackground(x, y, initX, initY) {
      distanceX = x - initX;
      distanceY = y - initY;
      ctx.clearRect(0, 0, c.width, c.height);
      var bgImage = document.getElementById("bg")
      var pattern = ctx.createPattern(bgImage, "repeat");
      ctx.rect(0, 0, width, height);
      ctx.fillStyle = pattern;
      ctx.fill();
      ctx.translate(Math.sqrt(distanceX), Math.sqrt(distanceY));
      }

      html,
      body {
      overflow: hidden;
      }

      #canvas {
      top: 0;
      left: 0;
      position: absolute;
      }

      <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
      <canvas id="canvas"></canvas>
      <img src="https://i.imgur.com/2MupHjw.png" id="bg" hidden>





      This method does not allow me to scroll left or up. It also speeds up the dragging after a distance and does not deal with negative motion well.






      var c = document.getElementById('canvas');
      var ctx = c.getContext('2d');
      var width = window.innerWidth;
      var height = window.innerHeight;

      c.width = width;
      c.height = height;

      drawGrid(width, height, 40);

      function drawGrid(gridWidth, gridHeight, boxSize) {
      ctx.clearRect(0, 0, c.width, c.height);
      ctx.beginPath();
      for (var i = 0; i <= gridWidth; i += boxSize) {
      ctx.moveTo(i, 0);
      ctx.lineTo(i, gridHeight);
      }
      for (var i = 0; i <= gridHeight; i += boxSize) {
      ctx.moveTo(0, i);
      ctx.lineTo(gridWidth, i);
      }
      ctx.strokeStyle = "rgba( 210, 210, 210, 1 )";
      ctx.stroke();
      }

      html,
      body {
      overflow: hidden;
      }

      #canvas {
      position: absolute;
      top: 0;
      left: 0;
      }

      <canvas id="canvas"></canvas>





      var c = document.getElementById('canvas');
      var ctx = c.getContext('2d');
      var width = window.innerWidth;
      var height = window.innerHeight;

      c.width = width;
      c.height = height;

      drawGrid(width, height, 40);

      function drawGrid(gridWidth, gridHeight, boxSize) {
      ctx.clearRect(0, 0, c.width, c.height);
      ctx.beginPath();
      for (var i = 0; i <= gridWidth; i += boxSize) {
      ctx.moveTo(i, 0);
      ctx.lineTo(i, gridHeight);
      }
      for (var i = 0; i <= gridHeight; i += boxSize) {
      ctx.moveTo(0, i);
      ctx.lineTo(gridWidth, i);
      }
      ctx.strokeStyle = "rgba( 210, 210, 210, 1 )";
      ctx.stroke();
      }

      html,
      body {
      overflow: hidden;
      }

      #canvas {
      position: absolute;
      top: 0;
      left: 0;
      }

      <canvas id="canvas"></canvas>





      var c = document.getElementById("canvas"),
      ctx = c.getContext("2d");
      var width = window.innerWidth;
      var height = window.innerHeight;
      var itemIsSelected = false;
      var clicked = function(e) {
      var x = e.pageX;
      var y = e.pageY;
      }

      draw(width, height);
      draggable('#app');

      function draw(width, height) {
      c.width = width;
      c.height = height;
      generateBackground();
      }

      function draggable(item) {
      var isMouseDown = false;
      document.onmousedown = function(e) {
      e.preventDefault();
      clicked.x = e.pageX;
      clicked.y = e.pageY;
      $(item).css('cursor', 'all-scroll');
      isMouseDown = true;
      };
      document.onmouseup = function(e) {
      e.preventDefault();
      isMouseDown = false;
      $(item).css('cursor', 'default');
      };
      document.onmousemove = function(e) {
      e.preventDefault();
      if (isMouseDown == true) {
      var mouseX = e.pageX;
      var mouseY = e.pageY;
      generateBackground(mouseX, mouseY, clicked.x, clicked.y);
      }
      };
      }

      function generateBackground(x, y, initX, initY) {
      distanceX = x - initX;
      distanceY = y - initY;
      ctx.clearRect(0, 0, c.width, c.height);
      var bgImage = document.getElementById("bg")
      var pattern = ctx.createPattern(bgImage, "repeat");
      ctx.rect(0, 0, width, height);
      ctx.fillStyle = pattern;
      ctx.fill();
      ctx.translate(Math.sqrt(distanceX), Math.sqrt(distanceY));
      }

      html,
      body {
      overflow: hidden;
      }

      #canvas {
      top: 0;
      left: 0;
      position: absolute;
      }

      <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
      <canvas id="canvas"></canvas>
      <img src="https://i.imgur.com/2MupHjw.png" id="bg" hidden>





      var c = document.getElementById("canvas"),
      ctx = c.getContext("2d");
      var width = window.innerWidth;
      var height = window.innerHeight;
      var itemIsSelected = false;
      var clicked = function(e) {
      var x = e.pageX;
      var y = e.pageY;
      }

      draw(width, height);
      draggable('#app');

      function draw(width, height) {
      c.width = width;
      c.height = height;
      generateBackground();
      }

      function draggable(item) {
      var isMouseDown = false;
      document.onmousedown = function(e) {
      e.preventDefault();
      clicked.x = e.pageX;
      clicked.y = e.pageY;
      $(item).css('cursor', 'all-scroll');
      isMouseDown = true;
      };
      document.onmouseup = function(e) {
      e.preventDefault();
      isMouseDown = false;
      $(item).css('cursor', 'default');
      };
      document.onmousemove = function(e) {
      e.preventDefault();
      if (isMouseDown == true) {
      var mouseX = e.pageX;
      var mouseY = e.pageY;
      generateBackground(mouseX, mouseY, clicked.x, clicked.y);
      }
      };
      }

      function generateBackground(x, y, initX, initY) {
      distanceX = x - initX;
      distanceY = y - initY;
      ctx.clearRect(0, 0, c.width, c.height);
      var bgImage = document.getElementById("bg")
      var pattern = ctx.createPattern(bgImage, "repeat");
      ctx.rect(0, 0, width, height);
      ctx.fillStyle = pattern;
      ctx.fill();
      ctx.translate(Math.sqrt(distanceX), Math.sqrt(distanceY));
      }

      html,
      body {
      overflow: hidden;
      }

      #canvas {
      top: 0;
      left: 0;
      position: absolute;
      }

      <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
      <canvas id="canvas"></canvas>
      <img src="https://i.imgur.com/2MupHjw.png" id="bg" hidden>






      javascript jquery html html5-canvas






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 15 '18 at 3:43









      JP4

      111513




      111513










      asked Nov 14 '18 at 23:11









      Tom JaquoTom Jaquo

      308




      308
























          1 Answer
          1






          active

          oldest

          votes


















          4














          Pan and Zoom



          I dont have much tine so the code will have to do most of the explaining.



          The example below pans and zooms using the mouse and mouse wheel.



          The object panZoom holds the information regarding the zoom (scale) and pan position (x, y)



          To pan just use the change in mouse position to change the panZoom x, y position. You dont need to scale the mouse movements as they are not effected by the zoom.



          The zoom is via a function panZoom.scaleAt(x,y,scale) where x, y is the mouse position and scaleBy is the amount to scale the scale by. Eg panZoom.scaleAt(100,100, 2) will zoom in 2 times at position 100,100 and panZoom.scaleAt(100,100, 1/2) will zoom out 2 times at the same position. See update function for more details.



          To draw in the panZoom coordinate system you need to call the function panZoom.apply which sets the context transform to match panZoom's settings. The function drawGrid is an example. It draws a grid to fit the current pan and zoom.



          Note that to restore the normal screen coordinates just call ctx.setTransform(1,0,0,1,0,0) which you will need to do if you want to clear the canvas.






          const ctx = canvas.getContext("2d");
          requestAnimationFrame(update);
          const mouse = {x : 0, y : 0, button : false, wheel : 0, lastX : 0, lastY : 0, drag : false};


          function mouseEvents(e){
          const bounds = canvas.getBoundingClientRect();
          mouse.x = e.pageX - bounds.left - scrollX;
          mouse.y = e.pageY - bounds.top - scrollY;
          mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
          if(e.type === "wheel"){
          mouse.wheel += -e.deltaY;
          e.preventDefault();
          }
          }
          ["mousedown", "mouseup", "mousemove", "wheel"].forEach(name => document.addEventListener(name,mouseEvents));


          const panZoom = {
          x : 0,
          y : 0,
          scale : 1,
          apply() { ctx.setTransform(this.scale, 0, 0, this.scale, this.x, this.y) },
          scaleAt(x, y, sc) { // x & y are screen coords, not world
          this.scale *= sc;
          this.x = x - (x - this.x) * sc;
          this.y = y - (y - this.y) * sc;
          },
          }

          function drawGrid(){
          const scale = 1 / panZoom.scale;
          var gridScale = 2 ** (Math.log2(128 * scale) | 0);
          var size = Math.max(w, h) * scale + gridScale * 2;
          var x = ((-panZoom.x * scale - gridScale) / gridScale | 0) * gridScale;
          var y = ((-panZoom.y * scale - gridScale) / gridScale | 0) * gridScale;
          panZoom.apply();
          ctx.lineWidth = 1;
          ctx.strokeStyle = "black";
          ctx.beginPath();
          for (i = 0; i < size; i += gridScale) {
          ctx.moveTo(x + i, y);
          ctx.lineTo(x + i, y + size);
          ctx.moveTo(x, y + i);
          ctx.lineTo(x + size, y + i);
          }
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform so the lineWidth is 1
          ctx.stroke();
          }


          var w = canvas.width;
          var h = canvas.height;
          function update(){
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
          ctx.globalAlpha = 1; // reset alpha
          if (w !== innerWidth || h !== innerHeight) {
          w = canvas.width = innerWidth;
          h = canvas.height = innerHeight;
          } else {
          ctx.clearRect(0, 0, w, h);
          }
          if (mouse.wheel !== 0) {
          let scale = 1;
          scale = mouse.wheel < 0 ? 1 / 1.01 : 1.01;
          mouse.wheel *= 0.8;
          if(Math.abs(mouse.wheel) < 1){
          mouse.wheel = 0;
          }
          panZoom.scaleAt(mouse.x, mouse.y, scale); //scale is the change in scale
          }
          if(mouse.button){
          if(!mouse.drag){
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          mouse.drag = true;
          } else {
          panZoom.x += mouse.x - mouse.lastX;
          panZoom.y += mouse.y - mouse.lastY;
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          }
          }else if(mouse.drag){
          mouse.drag = false;
          }
          drawGrid();
          requestAnimationFrame(update);
          }

          canvas { position : absolute; top : 0px; left : 0px; }

          <canvas id="canvas"></canvas>








          share|improve this answer
























          • This is better than I wanted! I was going to try to make the extra lines appear on my own.

            – Tom Jaquo
            Nov 16 '18 at 2:41











          • If I needed to, how do I prevent the subdivision of the grid when zoomed in, so only the same grid lines are there the whole time, getting larger when zoomed in or smaller when zoomed out?

            – Tom Jaquo
            Nov 17 '18 at 14:14











          • @TomJaquo Change the second line in the function drawGrid to var gridScale = 128; The 128 is in pixels and can be any value. Warning the code will try to draw all visible grid lines, which can quickly mount up to a lot of lines when zooming out.

            – Blindman67
            Nov 17 '18 at 17:07











          • One more follow-up I can't figure out. How do I set a max and min scroll distance for the grid? Adding a size limit on the drawGrid function just stops drawing the graph instead of freezing the max or min scale of the graph.

            – Tom Jaquo
            Nov 19 '18 at 14:14






          • 1





            To limit the scale add scale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale; and scale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale; above the line ` panZoom.scaleAt(mouse.x,...` in the update function. Define minScale and maxScale to the limits you want. minScale the smaller value represents zooming in and maxScale the larger value represents zooming out.

            – Blindman67
            Nov 19 '18 at 17:21











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53310138%2fcreating-a-draggable-and-scaleable-grid-in-html5%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          4














          Pan and Zoom



          I dont have much tine so the code will have to do most of the explaining.



          The example below pans and zooms using the mouse and mouse wheel.



          The object panZoom holds the information regarding the zoom (scale) and pan position (x, y)



          To pan just use the change in mouse position to change the panZoom x, y position. You dont need to scale the mouse movements as they are not effected by the zoom.



          The zoom is via a function panZoom.scaleAt(x,y,scale) where x, y is the mouse position and scaleBy is the amount to scale the scale by. Eg panZoom.scaleAt(100,100, 2) will zoom in 2 times at position 100,100 and panZoom.scaleAt(100,100, 1/2) will zoom out 2 times at the same position. See update function for more details.



          To draw in the panZoom coordinate system you need to call the function panZoom.apply which sets the context transform to match panZoom's settings. The function drawGrid is an example. It draws a grid to fit the current pan and zoom.



          Note that to restore the normal screen coordinates just call ctx.setTransform(1,0,0,1,0,0) which you will need to do if you want to clear the canvas.






          const ctx = canvas.getContext("2d");
          requestAnimationFrame(update);
          const mouse = {x : 0, y : 0, button : false, wheel : 0, lastX : 0, lastY : 0, drag : false};


          function mouseEvents(e){
          const bounds = canvas.getBoundingClientRect();
          mouse.x = e.pageX - bounds.left - scrollX;
          mouse.y = e.pageY - bounds.top - scrollY;
          mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
          if(e.type === "wheel"){
          mouse.wheel += -e.deltaY;
          e.preventDefault();
          }
          }
          ["mousedown", "mouseup", "mousemove", "wheel"].forEach(name => document.addEventListener(name,mouseEvents));


          const panZoom = {
          x : 0,
          y : 0,
          scale : 1,
          apply() { ctx.setTransform(this.scale, 0, 0, this.scale, this.x, this.y) },
          scaleAt(x, y, sc) { // x & y are screen coords, not world
          this.scale *= sc;
          this.x = x - (x - this.x) * sc;
          this.y = y - (y - this.y) * sc;
          },
          }

          function drawGrid(){
          const scale = 1 / panZoom.scale;
          var gridScale = 2 ** (Math.log2(128 * scale) | 0);
          var size = Math.max(w, h) * scale + gridScale * 2;
          var x = ((-panZoom.x * scale - gridScale) / gridScale | 0) * gridScale;
          var y = ((-panZoom.y * scale - gridScale) / gridScale | 0) * gridScale;
          panZoom.apply();
          ctx.lineWidth = 1;
          ctx.strokeStyle = "black";
          ctx.beginPath();
          for (i = 0; i < size; i += gridScale) {
          ctx.moveTo(x + i, y);
          ctx.lineTo(x + i, y + size);
          ctx.moveTo(x, y + i);
          ctx.lineTo(x + size, y + i);
          }
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform so the lineWidth is 1
          ctx.stroke();
          }


          var w = canvas.width;
          var h = canvas.height;
          function update(){
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
          ctx.globalAlpha = 1; // reset alpha
          if (w !== innerWidth || h !== innerHeight) {
          w = canvas.width = innerWidth;
          h = canvas.height = innerHeight;
          } else {
          ctx.clearRect(0, 0, w, h);
          }
          if (mouse.wheel !== 0) {
          let scale = 1;
          scale = mouse.wheel < 0 ? 1 / 1.01 : 1.01;
          mouse.wheel *= 0.8;
          if(Math.abs(mouse.wheel) < 1){
          mouse.wheel = 0;
          }
          panZoom.scaleAt(mouse.x, mouse.y, scale); //scale is the change in scale
          }
          if(mouse.button){
          if(!mouse.drag){
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          mouse.drag = true;
          } else {
          panZoom.x += mouse.x - mouse.lastX;
          panZoom.y += mouse.y - mouse.lastY;
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          }
          }else if(mouse.drag){
          mouse.drag = false;
          }
          drawGrid();
          requestAnimationFrame(update);
          }

          canvas { position : absolute; top : 0px; left : 0px; }

          <canvas id="canvas"></canvas>








          share|improve this answer
























          • This is better than I wanted! I was going to try to make the extra lines appear on my own.

            – Tom Jaquo
            Nov 16 '18 at 2:41











          • If I needed to, how do I prevent the subdivision of the grid when zoomed in, so only the same grid lines are there the whole time, getting larger when zoomed in or smaller when zoomed out?

            – Tom Jaquo
            Nov 17 '18 at 14:14











          • @TomJaquo Change the second line in the function drawGrid to var gridScale = 128; The 128 is in pixels and can be any value. Warning the code will try to draw all visible grid lines, which can quickly mount up to a lot of lines when zooming out.

            – Blindman67
            Nov 17 '18 at 17:07











          • One more follow-up I can't figure out. How do I set a max and min scroll distance for the grid? Adding a size limit on the drawGrid function just stops drawing the graph instead of freezing the max or min scale of the graph.

            – Tom Jaquo
            Nov 19 '18 at 14:14






          • 1





            To limit the scale add scale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale; and scale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale; above the line ` panZoom.scaleAt(mouse.x,...` in the update function. Define minScale and maxScale to the limits you want. minScale the smaller value represents zooming in and maxScale the larger value represents zooming out.

            – Blindman67
            Nov 19 '18 at 17:21
















          4














          Pan and Zoom



          I dont have much tine so the code will have to do most of the explaining.



          The example below pans and zooms using the mouse and mouse wheel.



          The object panZoom holds the information regarding the zoom (scale) and pan position (x, y)



          To pan just use the change in mouse position to change the panZoom x, y position. You dont need to scale the mouse movements as they are not effected by the zoom.



          The zoom is via a function panZoom.scaleAt(x,y,scale) where x, y is the mouse position and scaleBy is the amount to scale the scale by. Eg panZoom.scaleAt(100,100, 2) will zoom in 2 times at position 100,100 and panZoom.scaleAt(100,100, 1/2) will zoom out 2 times at the same position. See update function for more details.



          To draw in the panZoom coordinate system you need to call the function panZoom.apply which sets the context transform to match panZoom's settings. The function drawGrid is an example. It draws a grid to fit the current pan and zoom.



          Note that to restore the normal screen coordinates just call ctx.setTransform(1,0,0,1,0,0) which you will need to do if you want to clear the canvas.






          const ctx = canvas.getContext("2d");
          requestAnimationFrame(update);
          const mouse = {x : 0, y : 0, button : false, wheel : 0, lastX : 0, lastY : 0, drag : false};


          function mouseEvents(e){
          const bounds = canvas.getBoundingClientRect();
          mouse.x = e.pageX - bounds.left - scrollX;
          mouse.y = e.pageY - bounds.top - scrollY;
          mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
          if(e.type === "wheel"){
          mouse.wheel += -e.deltaY;
          e.preventDefault();
          }
          }
          ["mousedown", "mouseup", "mousemove", "wheel"].forEach(name => document.addEventListener(name,mouseEvents));


          const panZoom = {
          x : 0,
          y : 0,
          scale : 1,
          apply() { ctx.setTransform(this.scale, 0, 0, this.scale, this.x, this.y) },
          scaleAt(x, y, sc) { // x & y are screen coords, not world
          this.scale *= sc;
          this.x = x - (x - this.x) * sc;
          this.y = y - (y - this.y) * sc;
          },
          }

          function drawGrid(){
          const scale = 1 / panZoom.scale;
          var gridScale = 2 ** (Math.log2(128 * scale) | 0);
          var size = Math.max(w, h) * scale + gridScale * 2;
          var x = ((-panZoom.x * scale - gridScale) / gridScale | 0) * gridScale;
          var y = ((-panZoom.y * scale - gridScale) / gridScale | 0) * gridScale;
          panZoom.apply();
          ctx.lineWidth = 1;
          ctx.strokeStyle = "black";
          ctx.beginPath();
          for (i = 0; i < size; i += gridScale) {
          ctx.moveTo(x + i, y);
          ctx.lineTo(x + i, y + size);
          ctx.moveTo(x, y + i);
          ctx.lineTo(x + size, y + i);
          }
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform so the lineWidth is 1
          ctx.stroke();
          }


          var w = canvas.width;
          var h = canvas.height;
          function update(){
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
          ctx.globalAlpha = 1; // reset alpha
          if (w !== innerWidth || h !== innerHeight) {
          w = canvas.width = innerWidth;
          h = canvas.height = innerHeight;
          } else {
          ctx.clearRect(0, 0, w, h);
          }
          if (mouse.wheel !== 0) {
          let scale = 1;
          scale = mouse.wheel < 0 ? 1 / 1.01 : 1.01;
          mouse.wheel *= 0.8;
          if(Math.abs(mouse.wheel) < 1){
          mouse.wheel = 0;
          }
          panZoom.scaleAt(mouse.x, mouse.y, scale); //scale is the change in scale
          }
          if(mouse.button){
          if(!mouse.drag){
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          mouse.drag = true;
          } else {
          panZoom.x += mouse.x - mouse.lastX;
          panZoom.y += mouse.y - mouse.lastY;
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          }
          }else if(mouse.drag){
          mouse.drag = false;
          }
          drawGrid();
          requestAnimationFrame(update);
          }

          canvas { position : absolute; top : 0px; left : 0px; }

          <canvas id="canvas"></canvas>








          share|improve this answer
























          • This is better than I wanted! I was going to try to make the extra lines appear on my own.

            – Tom Jaquo
            Nov 16 '18 at 2:41











          • If I needed to, how do I prevent the subdivision of the grid when zoomed in, so only the same grid lines are there the whole time, getting larger when zoomed in or smaller when zoomed out?

            – Tom Jaquo
            Nov 17 '18 at 14:14











          • @TomJaquo Change the second line in the function drawGrid to var gridScale = 128; The 128 is in pixels and can be any value. Warning the code will try to draw all visible grid lines, which can quickly mount up to a lot of lines when zooming out.

            – Blindman67
            Nov 17 '18 at 17:07











          • One more follow-up I can't figure out. How do I set a max and min scroll distance for the grid? Adding a size limit on the drawGrid function just stops drawing the graph instead of freezing the max or min scale of the graph.

            – Tom Jaquo
            Nov 19 '18 at 14:14






          • 1





            To limit the scale add scale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale; and scale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale; above the line ` panZoom.scaleAt(mouse.x,...` in the update function. Define minScale and maxScale to the limits you want. minScale the smaller value represents zooming in and maxScale the larger value represents zooming out.

            – Blindman67
            Nov 19 '18 at 17:21














          4












          4








          4







          Pan and Zoom



          I dont have much tine so the code will have to do most of the explaining.



          The example below pans and zooms using the mouse and mouse wheel.



          The object panZoom holds the information regarding the zoom (scale) and pan position (x, y)



          To pan just use the change in mouse position to change the panZoom x, y position. You dont need to scale the mouse movements as they are not effected by the zoom.



          The zoom is via a function panZoom.scaleAt(x,y,scale) where x, y is the mouse position and scaleBy is the amount to scale the scale by. Eg panZoom.scaleAt(100,100, 2) will zoom in 2 times at position 100,100 and panZoom.scaleAt(100,100, 1/2) will zoom out 2 times at the same position. See update function for more details.



          To draw in the panZoom coordinate system you need to call the function panZoom.apply which sets the context transform to match panZoom's settings. The function drawGrid is an example. It draws a grid to fit the current pan and zoom.



          Note that to restore the normal screen coordinates just call ctx.setTransform(1,0,0,1,0,0) which you will need to do if you want to clear the canvas.






          const ctx = canvas.getContext("2d");
          requestAnimationFrame(update);
          const mouse = {x : 0, y : 0, button : false, wheel : 0, lastX : 0, lastY : 0, drag : false};


          function mouseEvents(e){
          const bounds = canvas.getBoundingClientRect();
          mouse.x = e.pageX - bounds.left - scrollX;
          mouse.y = e.pageY - bounds.top - scrollY;
          mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
          if(e.type === "wheel"){
          mouse.wheel += -e.deltaY;
          e.preventDefault();
          }
          }
          ["mousedown", "mouseup", "mousemove", "wheel"].forEach(name => document.addEventListener(name,mouseEvents));


          const panZoom = {
          x : 0,
          y : 0,
          scale : 1,
          apply() { ctx.setTransform(this.scale, 0, 0, this.scale, this.x, this.y) },
          scaleAt(x, y, sc) { // x & y are screen coords, not world
          this.scale *= sc;
          this.x = x - (x - this.x) * sc;
          this.y = y - (y - this.y) * sc;
          },
          }

          function drawGrid(){
          const scale = 1 / panZoom.scale;
          var gridScale = 2 ** (Math.log2(128 * scale) | 0);
          var size = Math.max(w, h) * scale + gridScale * 2;
          var x = ((-panZoom.x * scale - gridScale) / gridScale | 0) * gridScale;
          var y = ((-panZoom.y * scale - gridScale) / gridScale | 0) * gridScale;
          panZoom.apply();
          ctx.lineWidth = 1;
          ctx.strokeStyle = "black";
          ctx.beginPath();
          for (i = 0; i < size; i += gridScale) {
          ctx.moveTo(x + i, y);
          ctx.lineTo(x + i, y + size);
          ctx.moveTo(x, y + i);
          ctx.lineTo(x + size, y + i);
          }
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform so the lineWidth is 1
          ctx.stroke();
          }


          var w = canvas.width;
          var h = canvas.height;
          function update(){
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
          ctx.globalAlpha = 1; // reset alpha
          if (w !== innerWidth || h !== innerHeight) {
          w = canvas.width = innerWidth;
          h = canvas.height = innerHeight;
          } else {
          ctx.clearRect(0, 0, w, h);
          }
          if (mouse.wheel !== 0) {
          let scale = 1;
          scale = mouse.wheel < 0 ? 1 / 1.01 : 1.01;
          mouse.wheel *= 0.8;
          if(Math.abs(mouse.wheel) < 1){
          mouse.wheel = 0;
          }
          panZoom.scaleAt(mouse.x, mouse.y, scale); //scale is the change in scale
          }
          if(mouse.button){
          if(!mouse.drag){
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          mouse.drag = true;
          } else {
          panZoom.x += mouse.x - mouse.lastX;
          panZoom.y += mouse.y - mouse.lastY;
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          }
          }else if(mouse.drag){
          mouse.drag = false;
          }
          drawGrid();
          requestAnimationFrame(update);
          }

          canvas { position : absolute; top : 0px; left : 0px; }

          <canvas id="canvas"></canvas>








          share|improve this answer













          Pan and Zoom



          I dont have much tine so the code will have to do most of the explaining.



          The example below pans and zooms using the mouse and mouse wheel.



          The object panZoom holds the information regarding the zoom (scale) and pan position (x, y)



          To pan just use the change in mouse position to change the panZoom x, y position. You dont need to scale the mouse movements as they are not effected by the zoom.



          The zoom is via a function panZoom.scaleAt(x,y,scale) where x, y is the mouse position and scaleBy is the amount to scale the scale by. Eg panZoom.scaleAt(100,100, 2) will zoom in 2 times at position 100,100 and panZoom.scaleAt(100,100, 1/2) will zoom out 2 times at the same position. See update function for more details.



          To draw in the panZoom coordinate system you need to call the function panZoom.apply which sets the context transform to match panZoom's settings. The function drawGrid is an example. It draws a grid to fit the current pan and zoom.



          Note that to restore the normal screen coordinates just call ctx.setTransform(1,0,0,1,0,0) which you will need to do if you want to clear the canvas.






          const ctx = canvas.getContext("2d");
          requestAnimationFrame(update);
          const mouse = {x : 0, y : 0, button : false, wheel : 0, lastX : 0, lastY : 0, drag : false};


          function mouseEvents(e){
          const bounds = canvas.getBoundingClientRect();
          mouse.x = e.pageX - bounds.left - scrollX;
          mouse.y = e.pageY - bounds.top - scrollY;
          mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
          if(e.type === "wheel"){
          mouse.wheel += -e.deltaY;
          e.preventDefault();
          }
          }
          ["mousedown", "mouseup", "mousemove", "wheel"].forEach(name => document.addEventListener(name,mouseEvents));


          const panZoom = {
          x : 0,
          y : 0,
          scale : 1,
          apply() { ctx.setTransform(this.scale, 0, 0, this.scale, this.x, this.y) },
          scaleAt(x, y, sc) { // x & y are screen coords, not world
          this.scale *= sc;
          this.x = x - (x - this.x) * sc;
          this.y = y - (y - this.y) * sc;
          },
          }

          function drawGrid(){
          const scale = 1 / panZoom.scale;
          var gridScale = 2 ** (Math.log2(128 * scale) | 0);
          var size = Math.max(w, h) * scale + gridScale * 2;
          var x = ((-panZoom.x * scale - gridScale) / gridScale | 0) * gridScale;
          var y = ((-panZoom.y * scale - gridScale) / gridScale | 0) * gridScale;
          panZoom.apply();
          ctx.lineWidth = 1;
          ctx.strokeStyle = "black";
          ctx.beginPath();
          for (i = 0; i < size; i += gridScale) {
          ctx.moveTo(x + i, y);
          ctx.lineTo(x + i, y + size);
          ctx.moveTo(x, y + i);
          ctx.lineTo(x + size, y + i);
          }
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform so the lineWidth is 1
          ctx.stroke();
          }


          var w = canvas.width;
          var h = canvas.height;
          function update(){
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
          ctx.globalAlpha = 1; // reset alpha
          if (w !== innerWidth || h !== innerHeight) {
          w = canvas.width = innerWidth;
          h = canvas.height = innerHeight;
          } else {
          ctx.clearRect(0, 0, w, h);
          }
          if (mouse.wheel !== 0) {
          let scale = 1;
          scale = mouse.wheel < 0 ? 1 / 1.01 : 1.01;
          mouse.wheel *= 0.8;
          if(Math.abs(mouse.wheel) < 1){
          mouse.wheel = 0;
          }
          panZoom.scaleAt(mouse.x, mouse.y, scale); //scale is the change in scale
          }
          if(mouse.button){
          if(!mouse.drag){
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          mouse.drag = true;
          } else {
          panZoom.x += mouse.x - mouse.lastX;
          panZoom.y += mouse.y - mouse.lastY;
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          }
          }else if(mouse.drag){
          mouse.drag = false;
          }
          drawGrid();
          requestAnimationFrame(update);
          }

          canvas { position : absolute; top : 0px; left : 0px; }

          <canvas id="canvas"></canvas>








          const ctx = canvas.getContext("2d");
          requestAnimationFrame(update);
          const mouse = {x : 0, y : 0, button : false, wheel : 0, lastX : 0, lastY : 0, drag : false};


          function mouseEvents(e){
          const bounds = canvas.getBoundingClientRect();
          mouse.x = e.pageX - bounds.left - scrollX;
          mouse.y = e.pageY - bounds.top - scrollY;
          mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
          if(e.type === "wheel"){
          mouse.wheel += -e.deltaY;
          e.preventDefault();
          }
          }
          ["mousedown", "mouseup", "mousemove", "wheel"].forEach(name => document.addEventListener(name,mouseEvents));


          const panZoom = {
          x : 0,
          y : 0,
          scale : 1,
          apply() { ctx.setTransform(this.scale, 0, 0, this.scale, this.x, this.y) },
          scaleAt(x, y, sc) { // x & y are screen coords, not world
          this.scale *= sc;
          this.x = x - (x - this.x) * sc;
          this.y = y - (y - this.y) * sc;
          },
          }

          function drawGrid(){
          const scale = 1 / panZoom.scale;
          var gridScale = 2 ** (Math.log2(128 * scale) | 0);
          var size = Math.max(w, h) * scale + gridScale * 2;
          var x = ((-panZoom.x * scale - gridScale) / gridScale | 0) * gridScale;
          var y = ((-panZoom.y * scale - gridScale) / gridScale | 0) * gridScale;
          panZoom.apply();
          ctx.lineWidth = 1;
          ctx.strokeStyle = "black";
          ctx.beginPath();
          for (i = 0; i < size; i += gridScale) {
          ctx.moveTo(x + i, y);
          ctx.lineTo(x + i, y + size);
          ctx.moveTo(x, y + i);
          ctx.lineTo(x + size, y + i);
          }
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform so the lineWidth is 1
          ctx.stroke();
          }


          var w = canvas.width;
          var h = canvas.height;
          function update(){
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
          ctx.globalAlpha = 1; // reset alpha
          if (w !== innerWidth || h !== innerHeight) {
          w = canvas.width = innerWidth;
          h = canvas.height = innerHeight;
          } else {
          ctx.clearRect(0, 0, w, h);
          }
          if (mouse.wheel !== 0) {
          let scale = 1;
          scale = mouse.wheel < 0 ? 1 / 1.01 : 1.01;
          mouse.wheel *= 0.8;
          if(Math.abs(mouse.wheel) < 1){
          mouse.wheel = 0;
          }
          panZoom.scaleAt(mouse.x, mouse.y, scale); //scale is the change in scale
          }
          if(mouse.button){
          if(!mouse.drag){
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          mouse.drag = true;
          } else {
          panZoom.x += mouse.x - mouse.lastX;
          panZoom.y += mouse.y - mouse.lastY;
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          }
          }else if(mouse.drag){
          mouse.drag = false;
          }
          drawGrid();
          requestAnimationFrame(update);
          }

          canvas { position : absolute; top : 0px; left : 0px; }

          <canvas id="canvas"></canvas>





          const ctx = canvas.getContext("2d");
          requestAnimationFrame(update);
          const mouse = {x : 0, y : 0, button : false, wheel : 0, lastX : 0, lastY : 0, drag : false};


          function mouseEvents(e){
          const bounds = canvas.getBoundingClientRect();
          mouse.x = e.pageX - bounds.left - scrollX;
          mouse.y = e.pageY - bounds.top - scrollY;
          mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
          if(e.type === "wheel"){
          mouse.wheel += -e.deltaY;
          e.preventDefault();
          }
          }
          ["mousedown", "mouseup", "mousemove", "wheel"].forEach(name => document.addEventListener(name,mouseEvents));


          const panZoom = {
          x : 0,
          y : 0,
          scale : 1,
          apply() { ctx.setTransform(this.scale, 0, 0, this.scale, this.x, this.y) },
          scaleAt(x, y, sc) { // x & y are screen coords, not world
          this.scale *= sc;
          this.x = x - (x - this.x) * sc;
          this.y = y - (y - this.y) * sc;
          },
          }

          function drawGrid(){
          const scale = 1 / panZoom.scale;
          var gridScale = 2 ** (Math.log2(128 * scale) | 0);
          var size = Math.max(w, h) * scale + gridScale * 2;
          var x = ((-panZoom.x * scale - gridScale) / gridScale | 0) * gridScale;
          var y = ((-panZoom.y * scale - gridScale) / gridScale | 0) * gridScale;
          panZoom.apply();
          ctx.lineWidth = 1;
          ctx.strokeStyle = "black";
          ctx.beginPath();
          for (i = 0; i < size; i += gridScale) {
          ctx.moveTo(x + i, y);
          ctx.lineTo(x + i, y + size);
          ctx.moveTo(x, y + i);
          ctx.lineTo(x + size, y + i);
          }
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform so the lineWidth is 1
          ctx.stroke();
          }


          var w = canvas.width;
          var h = canvas.height;
          function update(){
          ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
          ctx.globalAlpha = 1; // reset alpha
          if (w !== innerWidth || h !== innerHeight) {
          w = canvas.width = innerWidth;
          h = canvas.height = innerHeight;
          } else {
          ctx.clearRect(0, 0, w, h);
          }
          if (mouse.wheel !== 0) {
          let scale = 1;
          scale = mouse.wheel < 0 ? 1 / 1.01 : 1.01;
          mouse.wheel *= 0.8;
          if(Math.abs(mouse.wheel) < 1){
          mouse.wheel = 0;
          }
          panZoom.scaleAt(mouse.x, mouse.y, scale); //scale is the change in scale
          }
          if(mouse.button){
          if(!mouse.drag){
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          mouse.drag = true;
          } else {
          panZoom.x += mouse.x - mouse.lastX;
          panZoom.y += mouse.y - mouse.lastY;
          mouse.lastX = mouse.x;
          mouse.lastY = mouse.y;
          }
          }else if(mouse.drag){
          mouse.drag = false;
          }
          drawGrid();
          requestAnimationFrame(update);
          }

          canvas { position : absolute; top : 0px; left : 0px; }

          <canvas id="canvas"></canvas>






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 16 '18 at 0:39









          Blindman67Blindman67

          26.7k52763




          26.7k52763













          • This is better than I wanted! I was going to try to make the extra lines appear on my own.

            – Tom Jaquo
            Nov 16 '18 at 2:41











          • If I needed to, how do I prevent the subdivision of the grid when zoomed in, so only the same grid lines are there the whole time, getting larger when zoomed in or smaller when zoomed out?

            – Tom Jaquo
            Nov 17 '18 at 14:14











          • @TomJaquo Change the second line in the function drawGrid to var gridScale = 128; The 128 is in pixels and can be any value. Warning the code will try to draw all visible grid lines, which can quickly mount up to a lot of lines when zooming out.

            – Blindman67
            Nov 17 '18 at 17:07











          • One more follow-up I can't figure out. How do I set a max and min scroll distance for the grid? Adding a size limit on the drawGrid function just stops drawing the graph instead of freezing the max or min scale of the graph.

            – Tom Jaquo
            Nov 19 '18 at 14:14






          • 1





            To limit the scale add scale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale; and scale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale; above the line ` panZoom.scaleAt(mouse.x,...` in the update function. Define minScale and maxScale to the limits you want. minScale the smaller value represents zooming in and maxScale the larger value represents zooming out.

            – Blindman67
            Nov 19 '18 at 17:21



















          • This is better than I wanted! I was going to try to make the extra lines appear on my own.

            – Tom Jaquo
            Nov 16 '18 at 2:41











          • If I needed to, how do I prevent the subdivision of the grid when zoomed in, so only the same grid lines are there the whole time, getting larger when zoomed in or smaller when zoomed out?

            – Tom Jaquo
            Nov 17 '18 at 14:14











          • @TomJaquo Change the second line in the function drawGrid to var gridScale = 128; The 128 is in pixels and can be any value. Warning the code will try to draw all visible grid lines, which can quickly mount up to a lot of lines when zooming out.

            – Blindman67
            Nov 17 '18 at 17:07











          • One more follow-up I can't figure out. How do I set a max and min scroll distance for the grid? Adding a size limit on the drawGrid function just stops drawing the graph instead of freezing the max or min scale of the graph.

            – Tom Jaquo
            Nov 19 '18 at 14:14






          • 1





            To limit the scale add scale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale; and scale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale; above the line ` panZoom.scaleAt(mouse.x,...` in the update function. Define minScale and maxScale to the limits you want. minScale the smaller value represents zooming in and maxScale the larger value represents zooming out.

            – Blindman67
            Nov 19 '18 at 17:21

















          This is better than I wanted! I was going to try to make the extra lines appear on my own.

          – Tom Jaquo
          Nov 16 '18 at 2:41





          This is better than I wanted! I was going to try to make the extra lines appear on my own.

          – Tom Jaquo
          Nov 16 '18 at 2:41













          If I needed to, how do I prevent the subdivision of the grid when zoomed in, so only the same grid lines are there the whole time, getting larger when zoomed in or smaller when zoomed out?

          – Tom Jaquo
          Nov 17 '18 at 14:14





          If I needed to, how do I prevent the subdivision of the grid when zoomed in, so only the same grid lines are there the whole time, getting larger when zoomed in or smaller when zoomed out?

          – Tom Jaquo
          Nov 17 '18 at 14:14













          @TomJaquo Change the second line in the function drawGrid to var gridScale = 128; The 128 is in pixels and can be any value. Warning the code will try to draw all visible grid lines, which can quickly mount up to a lot of lines when zooming out.

          – Blindman67
          Nov 17 '18 at 17:07





          @TomJaquo Change the second line in the function drawGrid to var gridScale = 128; The 128 is in pixels and can be any value. Warning the code will try to draw all visible grid lines, which can quickly mount up to a lot of lines when zooming out.

          – Blindman67
          Nov 17 '18 at 17:07













          One more follow-up I can't figure out. How do I set a max and min scroll distance for the grid? Adding a size limit on the drawGrid function just stops drawing the graph instead of freezing the max or min scale of the graph.

          – Tom Jaquo
          Nov 19 '18 at 14:14





          One more follow-up I can't figure out. How do I set a max and min scroll distance for the grid? Adding a size limit on the drawGrid function just stops drawing the graph instead of freezing the max or min scale of the graph.

          – Tom Jaquo
          Nov 19 '18 at 14:14




          1




          1





          To limit the scale add scale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale; and scale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale; above the line ` panZoom.scaleAt(mouse.x,...` in the update function. Define minScale and maxScale to the limits you want. minScale the smaller value represents zooming in and maxScale the larger value represents zooming out.

          – Blindman67
          Nov 19 '18 at 17:21





          To limit the scale add scale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale; and scale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale; above the line ` panZoom.scaleAt(mouse.x,...` in the update function. Define minScale and maxScale to the limits you want. minScale the smaller value represents zooming in and maxScale the larger value represents zooming out.

          – Blindman67
          Nov 19 '18 at 17:21




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53310138%2fcreating-a-draggable-and-scaleable-grid-in-html5%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Xamarin.iOS Cant Deploy on Iphone

          Glorious Revolution

          Dulmage-Mendelsohn matrix decomposition in Python