Creating a draggable and scaleable grid in HTML5
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.
javascript jquery html html5-canvas
add a comment |
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.
javascript jquery html html5-canvas
add a comment |
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.
javascript jquery html html5-canvas
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
javascript jquery html html5-canvas
edited Nov 15 '18 at 3:43
JP4
111513
111513
asked Nov 14 '18 at 23:11
Tom JaquoTom Jaquo
308
308
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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>
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 functiondrawGrid
tovar 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 thedrawGrid
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 addscale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale
; andscale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale;
above the line ` panZoom.scaleAt(mouse.x,...` in theupdate
function. DefineminScale
andmaxScale
to the limits you want.minScale
the smaller value represents zooming in andmaxScale
the larger value represents zooming out.
– Blindman67
Nov 19 '18 at 17:21
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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>
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 functiondrawGrid
tovar 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 thedrawGrid
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 addscale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale
; andscale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale;
above the line ` panZoom.scaleAt(mouse.x,...` in theupdate
function. DefineminScale
andmaxScale
to the limits you want.minScale
the smaller value represents zooming in andmaxScale
the larger value represents zooming out.
– Blindman67
Nov 19 '18 at 17:21
add a comment |
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>
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 functiondrawGrid
tovar 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 thedrawGrid
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 addscale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale
; andscale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale;
above the line ` panZoom.scaleAt(mouse.x,...` in theupdate
function. DefineminScale
andmaxScale
to the limits you want.minScale
the smaller value represents zooming in andmaxScale
the larger value represents zooming out.
– Blindman67
Nov 19 '18 at 17:21
add a comment |
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>
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>
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 functiondrawGrid
tovar 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 thedrawGrid
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 addscale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale
; andscale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale;
above the line ` panZoom.scaleAt(mouse.x,...` in theupdate
function. DefineminScale
andmaxScale
to the limits you want.minScale
the smaller value represents zooming in andmaxScale
the larger value represents zooming out.
– Blindman67
Nov 19 '18 at 17:21
add a comment |
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 functiondrawGrid
tovar 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 thedrawGrid
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 addscale = panZoom.scale * scale < minScale ? minScale / panZoom.scale : scale
; andscale = panZoom.scale * scale > maxScale ? maxScale / panZoom.scale : scale;
above the line ` panZoom.scaleAt(mouse.x,...` in theupdate
function. DefineminScale
andmaxScale
to the limits you want.minScale
the smaller value represents zooming in andmaxScale
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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