After Dynamically resizing a canvas using javascript points plotted on it no longer line up with where they are placed, and also arcs are getting stretched from how they should be rendered.
The below runnable example demonstrates the issue, because what should be a point where you click the cursor turns into a large miss-shapen oval which is in the wrong place.
var TargetWidth = 400;
var canvases = $(".hotspot-canvas")
for (i = 0; i < canvases.length; i++) {
canvas = $(canvases[i]);
var src = canvas.attr("data-img");
initilizePlotPointCanvas(canvas, src);
}
function initilizePlotPointCanvas(canvas, src)
{
var my_image = new Image();
my_image.onload = function(){
var w1 = this.width;
var w2 = TargetWidth
var r = w1 / w2;
var h1 = this.height;
var h2 = this.height / r;
canvas.width(TargetWidth).height(h2).css("background-image", "url("+src+")");
setTimeout(function(){
var jcanvas = canvas[0];
var ctx = jcanvas.getContext('2d'),
w = jcanvas.width,
h = jcanvas.height;
ctx.translate(-0.1, -0.1);
jcanvas.onmousedown = function(e) {
var rect = jcanvas.getBoundingClientRect();
x3 = e.clientX - rect.left;
y3 = e.clientY - rect.top;
ctx.clearRect(0, 0, w, h)
ctx.beginPath();
ctx.arc(x3, y3, 5, 0, 2 * Math.PI, false);
ctx.fillStyle = 'red';
ctx.fill();
ctx.stroke();
}
}, 500)
}
my_image.src = src;
}
.hotspot-canvas {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
cursor: crosshair;
border: 1px solid black;
background-repeat: no-repeat;
background-position: center center;
background-clip: border-box;
background-origin: padding-box;
-moz-background-size: cover;
background-size: cover;
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<canvas
data-id="554924"
data-img="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQXXarydhE3CZRSMgXCProd1w0_oSwIOPd7zJN5EQmLtQtPDD21"
class="hotspot-canvas"
data-responses="0" data-notes="0" data-actions="0" data-email="123" data-responder="true" data-response-id="" orig_type="14" data-tag="6bbf8f97-758f-47ca-8b8c-24a1cd3ddd55" data-formtemplatequestiontype="s" data-valueifparentna="" data-exportkey=""></canvas>
Display Size and canvas resolution
Display size and canvas resolution are two different entities.
When you set the canvas style width and height you set the display size
canvas.style.width = "100px";
canvas.style.height = "100px";
When you set the canvas width and height you set the resolution.
canvas.width = 100;
canvas.height = 100;
You have set the display size but neglected to match the resolution to the display size.
You can fix it by just setting the resolution to match the display size
var bounds = jcanvas.getBoundingClientRect();
jcanvas.width = bounds.width;
jcanvas.height = bounds.height;
var TargetWidth = 400;
var canvases = $(".hotspot-canvas")
for (i = 0; i < canvases.length; i++) {
canvas = $(canvases[i]);
var src = canvas.attr("data-img");
initilizePlotPointCanvas(canvas, src);
}
function initilizePlotPointCanvas(canvas, src)
{
var my_image = new Image();
my_image.onload = function(){
var w1 = this.width;
var w2 = TargetWidth
var r = w1 / w2;
var h1 = this.height;
var h2 = this.height / r;
canvas.width(TargetWidth).height(h2).css("background-image", "url("+src+")");
setTimeout(function(){
var jcanvas = canvas[0];
var bounds = jcanvas.getBoundingClientRect();
jcanvas.width = bounds.width;
jcanvas.height = bounds.height;
var ctx = jcanvas.getContext('2d'),
w = jcanvas.width,
h = jcanvas.height;
ctx.translate(-0.1, -0.1);
jcanvas.onmousedown = function(e) {
var rect = jcanvas.getBoundingClientRect();
x3 = e.clientX - rect.left;
y3 = e.clientY - rect.top;
ctx.clearRect(0, 0, w, h)
ctx.beginPath();
ctx.arc(x3, y3, 5, 0, 2 * Math.PI, false);
ctx.fillStyle = 'red';
ctx.fill();
ctx.stroke();
}
}, 500)
}
my_image.src = src;
}
.hotspot-canvas {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
cursor: crosshair;
border: 1px solid black;
background-repeat: no-repeat;
background-position: center center;
background-clip: border-box;
background-origin: padding-box;
-moz-background-size: cover;
background-size: cover;
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<canvas
data-id="554924"
data-img="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQXXarydhE3CZRSMgXCProd1w0_oSwIOPd7zJN5EQmLtQtPDD21"
class="hotspot-canvas"
data-responses="0" data-notes="0" data-actions="0" data-email="123" data-responder="true" data-response-id="" orig_type="14" data-tag="6bbf8f97-758f-47ca-8b8c-24a1cd3ddd55" data-formtemplatequestiontype="s" data-valueifparentna="" data-exportkey=""></canvas>
Like I mentioned in my comment, I've run into this same problem quite recently, so I'd like to share my solution. Basically what's happening is your canvas element's dimensions are scaled and its origin is translated due to CSS, but the internal resolution of the canvas is still the same. Also, the border and padding of your canvas also affect the DOMRect's properties returned by getBoundingClientRect.
You need to take the clientX and clientY values from some MouseEvent or TouchEvent, the internal resolution of your canvas, and the dimensions and offset of the displayed canvas element, including border and padding, and use them to calculate the xy-coordinate relating to the canvas' internal coordinate system. This code requires JQuery and ES6 support, but you can compile down with Babel or something if you're worried about browser compatibility.
// translate coords in viewport to internal coords of canvas
const translateCoords = ({clientX, clientY}) => {
// dimensions of canvas element (top-left with respect to border-box)
const bcr = canvas.getBoundingClientRect();
// offsets from border and padding
const sideWidth = ["top", "right", "bottom", "left"].reduce((sum, side) => {
sum[side] = Object.values($(canvas).css([
`border-${side}-width`,
`padding-${side}`
])).reduce((a, b) => a + parseFloat(b), 0);
return sum;
}, {});
// ratio of internal canvas resolution to canvas displayed dimensions
const scaleX = canvas.width / (bcr.width - (sideWidth.left + sideWidth.right));
const scaleY = canvas.height / (bcr.height - (sideWidth.top + sideWidth.bottom));
// translate and scale screen coords to canvas internal coords
const x = (clientX - (bcr.left + sideWidth.left)) * scaleX;
const y = (clientY - (bcr.top + sideWidth.top)) * scaleY;
return {x, y};
};
// example event listener
canvas.addEventListener("click", (event) => {
const {x, y} = translateCoords(event);
// do something with translated coordinates
console.log(x, y);
});
Related
I have a canvas with some styling applied. There is a single letter centered in the canvas. Please take a look at the code below.
As the title suggest, I am trying to change the letter by pressing a key.
For example:
Letter A centered in canvas: I press the g - key, it changes to the letter g (Uppercase included)
As to my knowledge, I might have to use the method "keyup" with a "document.addEventListener".
Currently I am going through a course on learning JS, but I have noticed a strong reliance on certain libraries in the course, which I frankly dislike. I am not trashing the benefits, but I would prefer building a base with pure JS before using certain libraries I hardly understand. Some guidance would be appreciated.
body {
background-color: #000000;
}
canvas {
padding: 0;
margin: auto;
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: #111416;
border: 10px solid #a60000;
border-style: double;
box-shadow: 0 0 20px 5px #a60000;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<link rel="stylesheet" href="canvas.css">
<canvas id="myCanvas" width="800" height="800"></canvas>
<script>
// Get id from the canvas element
var canvas = document.getElementById("myCanvas");
// Provide 2D rendering context for the drawing surface of canvas
var context = canvas.getContext("2d");
// Get width and height of the canvas element
var canvW = document.getElementById("myCanvas").width;
var canvH = document.getElementById("myCanvas").height;
let text = "f";
context.fillStyle = "#a60000";
context.font = "700px serif";
// Measure the size of the letter and the specific font
// Always centers the letter regardless of size
// Display size of letter
const metrics = context.measureText(text);
const mx = metrics.actualBoundingBoxLeft * -1;
const my = metrics.actualBoundingBoxAscent * -1;
const mw = metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight;
const mh = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
const x = (canvW -mw) *0.5 - mx;
const y = (canvH - mh) *0.5 - my;
context.save();
context.translate(x, y);
context.beginPath();
context.rect(mx, my, mw, mh);
context.stroke();
context.fillText(text, 0, 0);
context.restore();
const onKeyUp = (e) => {
text = e.key.toUpperCase();
manager.render();
};
document.addEventListener("keyup", onKeyUp);
</script>
</body>
</html>
Your function call manager.render() results in an error as it's not present in the provided code snippet. I created one and moved your drawing stuff into it. The render function takes the input as an argument. Besides that I just had to add clearRect() to prevent overlapping letters.
// Get id from the canvas element
var canvas = document.getElementById("myCanvas");
// Provide 2D rendering context for the drawing surface of canvas
var context = canvas.getContext("2d");
// Get width and height of the canvas element
var canvW = document.getElementById("myCanvas").width;
var canvH = document.getElementById("myCanvas").height;
context.fillStyle = "#a60000";
context.font = "700px serif";
render("t");
function render(text) {
// Measure the size of the letter and the specific font
// Always centers the letter regardless of size
// Display size of letter
const metrics = context.measureText(text);
const mx = metrics.actualBoundingBoxLeft * -1;
const my = metrics.actualBoundingBoxAscent * -1;
const mw = metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight;
const mh = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
const x = (canvW -mw) *0.5 - mx;
const y = (canvH - mh) *0.5 - my;
context.clearRect(0,0,canvas.width,canvas.height)
context.save();
context.translate(x, y);
context.beginPath();
context.rect(mx, my, mw, mh);
context.stroke();
context.fillText(text, 0, 0);
context.restore();
}
const onKeyUp = (e) => {
const text = e.key.toUpperCase();
render(text);
};
document.addEventListener("keyup", onKeyUp);
body {
background-color: #000000;
}
canvas {
padding: 0;
margin: auto;
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: #111416;
border: 10px solid #a60000;
border-style: double;
box-shadow: 0 0 20px 5px #a60000;
}
<canvas id="myCanvas" width="800" height="800"></canvas>
The problem is that you dont redraw the canvas again after the hitting the button. For that you have to call the method context.clearRect(0, 0, canvas.width, canvas.height);In the eventlistener you have to call a function to redraw by wrapping your logic into a function. Now you can pass to the function the text as parameter you want to have rendered. Working Example:
code:
const redraw = function (text) {
var canvas = document.getElementById('myCanvas');
// Provide 2D rendering context for the drawing surface of canvas
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
// Get width and height of the canvas element
var canvW = document.getElementById('myCanvas').width;
var canvH = document.getElementById('myCanvas').height;
context.fillStyle = '#a60000';
context.font = '700px serif';
// Measure the size of the letter and the specific font
// Always centers the letter regardless of size
// Display size of letter
const metrics = context.measureText(text);
const mx = metrics.actualBoundingBoxLeft * -1;
const my = metrics.actualBoundingBoxAscent * -1;
const mw = metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight;
const mh = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
const x = (canvW - mw) * 0.5 - mx;
const y = (canvH - mh) * 0.5 - my;
context.save();
context.translate(x, y);
context.beginPath();
context.rect(mx, my, mw, mh);
context.stroke();
context.fillText(text, 0, 0);
context.restore();
};
redraw('I');
const onKeyUp = (e) => {
text = e.key.toUpperCase();
redraw(text);
};
document.addEventListener('keyup', onKeyUp);
I am making a magnifying glass for a canvas application but ive run into an issue with scrolling.
Essentially the goal is to take a canvas and when an image is added (in this case a cgm image) and make a snapshot of it, scale it up and draw it on to a second smaller canvas that overlays it and follow the mouse (preferably a drag/mousedown and up, but i am using mousemove for testing purposes). I believe the issue is in the zoom.style.top & zoom.style.left NOTE on my main aplication I have a top margin of 70px so keep that in mind.
here is a quick example I wrote up
<!-- language: lang-js -->
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
var ox = canvas.width / 2;
var oy = canvas.height / 2;
ctx.font = "42px serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "blue";
ctx.fillRect(ox / 2, oy / 2, ox, oy);
function magnify() {
var main = document.getElementById("canvas1");
var ctx = main.getContext('2d')
var base64 = main.toDataURL('image/png', 0);
drawing = new Image();
drawing.onload = () => {
var zoom = document.getElementById("tCanvas");
var zoomCtx = zoom.getContext('2d');
zoomCtx.drawImage(drawing, 0, 0);
}
main.addEventListener("mousemove", function(e) {
var zoom = document.getElementById("tCanvas");
var zoomCtx = zoom.getContext('2d');
zoomCtx.clearRect(0, 0, zoom.width, zoom.height);
zoomCtx.drawImage(main, e.x, e.y, 200, 200, 0, 0, 300, 300);
zoom.style.top = e.pageY - 70 + "px"
zoom.style.left = e.pageX - 10 + "px"
e.pageY = -150
e.pageX = -150
zoom.style.display = "block";
});
main.addEventListener("mouseleave", function() {
var zoom = document.getElementById("tCanvas");
zoom.style.display = "none";
});
drawing.src = base64;
};
<canvas id="tCanvas" class="cgm" height="100" width="100" style="background-color:white; position: absolute; display: none; z-
index:1;border:1px solid red;"> </canvas>
<canvas tabindex=1 class="cgm" id="canvas1" style="position:relative; background:white;
left:0;right:0;margin:auto;z-index:1;margin-top:70px; "></canvas>
<p></p>
<button id="zoom" onclick="magnify();">Zoom</button>
Here's a fiddle for reference (I fixed the height to display the scroll issue).
JSFiddle
I simplified a lot of the code on your JSFiddle.
See if this is the behavior you are looking for:
const canvas = document.getElementById("canvas1");
const ctx = canvas.getContext("2d");
const zoom = document.getElementById("tCanvas");
const zoomCtx = zoom.getContext('2d');
for (let i = 10; i < 20; i++)
for (let j = -60; j < 800; j += 60)
ctx.fillText(j + i, i * 20 - 180, i * 9 + j);
function getMousePos(evt) {
var rect = canvas.getBoundingClientRect()
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
}
}
canvas.addEventListener("mousemove", function(e) {
zoomCtx.clearRect(0, 0, zoom.width, zoom.height);
let pos = getMousePos(e)
zoomCtx.drawImage(canvas, pos.x, pos.y, 200, 200, 0, 0, 300, 300);
zoom.style.top = e.pageY - 10 + "px"
zoom.style.left = e.pageX - 10 + "px"
zoom.style.display = "block";
});
canvas.addEventListener("mouseleave", function() {
zoom.style.display = "none";
});
#tCanvas {
border: 1px solid red;
display: none;
position: absolute;
background-color: #808080;
pointer-events: none;
}
#canvas1 {
background-color: #808080;
}
<canvas id="tCanvas" width=100 height=100></canvas>
<canvas id="canvas1" width=240 height=800></canvas>
I think that your issue was with pointer-events: none; the top canvas was preventing the events from reaching the bottom canvas.
Also you don't have to declare your document.getElementById(".. on every function, those should be global constants
For the issue with a long canvas we need to use canvas.getBoundingClientRect in the calculation to get the real position of the mouse in the canvas
Most other solutions I have found relate to images as the bounds and work through css, and I can't get them to work with a canvas.
I want to create the bounds by defining an arbitrary percent size of the canvas (e.g 2x - twice the size) that expands from the center of the canvas and then have the canvas view (red) move/pan around within the bounds (green) by mouse hovering inside the canvas - that it moves to all corners of the bounds from the center of the canvas.
like this behavior in terms of the direction with panning
http://jsfiddle.net/georgedyer/XWnUA/2/
Additionally, I would like
the solution to work with a responsive canvas sizes rather than fixed canvas sizes
the panning to be smooth using easing such as CubicInOut easing
to detect the mouse hover inside the circle drawn. Might have to change the coordinate from screen to world to get it to work properly. But maybe theirs an easier way?
formulas
Here's my attempt at figuring out the formulas to get it working! I'll just focus on just one axis (x-axis) from left to right of the mouse, but the same formulas should apply to the y-axis.
First I get the "percentX" of the mouse along the width. If the "percentX" is 0 then the "newX" for the pan will be the value of the leftSide, else if it's 1 then it'll be the rightSide of the boundsBorder (green). So the "percentX" determines which side of the boundsBorder/limit the canvas view will extend/move to.
Only problem is that this doesn't work. So my formula must be wrong. The mouse inside the circle event is also inaccurate because of my canvas translate. Combining everything together is difficult to figure out!
Code
<html>
<head>
<meta charset="UTF-8">
<title>Panning</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
body {
background: #eee;
padding: 20px 0;
}
.canvas-container {
height: 400px;
width: 100%;
margin-bottom: 40px;
border-radius: 10px;
background: #fff;
overflow: hidden;
}
canvas {
width: 100%;
height: inherit;
}
.container {
width: 60%;
margin: 0 auto;
}
</style>
</head>
<body>
<!-- CANVAS -->
<div class="container">
<div class="canvas-container">
<canvas id="canvas"></canvas>
</div>
</div>
<!-- SCRIPT -->
<script type="text/javascript">
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width = canvas.clientWidth;
var height = canvas.height = canvas.clientHeight;
var panX = 0, panY = 0;
var scaleBorderFactor = 2; /* scale factor for the bounds */
var mouse = {
x: 0,
y: 0
}
function Circle(x, y, radius, color){
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.draw = function(ctx){
ctx.beginPath();
ctx.arc(this.x,this.y,this.radius,0,Math.PI*2,false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
}
/* Rect Stroke Borders for visual reference */
function RectBorder(x, y, w, h, c){
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.color = c;
this.draw = function(ctx){
ctx.strokeStyle = this.color;
ctx.lineWidth = 2;
ctx.strokeRect(this.x, this.y, this.width, this.height);
};
/* Draw rect from a center point */
this.drawAtCenter = function(ctx){
ctx.strokeStyle = this.color;
ctx.lineWidth = 2;
ctx.strokeRect(this.x, this.y, this.width, this.height);
};
this.toString = function(){
console.log("x: "+this.x+", y: "+this.y+", w: "+this.width+", h: "+this.height+", color = "+this.color);
}
}
function getMousePos(canvas, event) {
var rect = canvas.getBoundingClientRect();
return {
x: (event.clientX - rect.left),
y: (event.clientY - rect.top)
};
}
function insideCircle(circle, mouse){
var dist = Math.sqrt(Math.pow(circle.x - mouse.x,2) + Math.pow(circle.y - mouse.y,2));
console.log(circle.radius+", d = "+dist)
return dist <= circle.radius;
}
function lerp(start, end, percent){
return start*(1 - percent) + percent*end;
}
/* t: current time, b: beginning value, c: change in value, d: duration */
function easeInOutCubic(t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
}
var canvasBorder = new RectBorder(0,0,canvas.width,canvas.height,'red');
var boundsBorder = new RectBorder(-(canvas.width*scaleBorderFactor - canvas.width)/2,-(canvas.height*scaleBorderFactor - canvas.height)/2,canvas.width*scaleBorderFactor,canvas.height*scaleBorderFactor,'green');
var circle = new Circle(200,200,40,'blue');
/* Draw Update */
update();
function update(){
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
width = canvas.width = canvas.clientWidth;
height = canvas.height = canvas.clientHeight;
ctx.clearRect(0,0,width,height);
ctx.save();
/* MOUSE */
percentX = (mouse.x / width);
percentY = (mouse.y / height);
/* the 2 sides of boundsBorder */
var leftSide = (width*scaleBorderFactor - width)/2 - width;
var rightSide = (width*scaleBorderFactor - width)/2 + width;
var topSide = (height*scaleBorderFactor - height)/2 - width;
var bottomSide = (height*scaleBorderFactor - height)/2 + height;
newX = rightSide * percentX + leftSide * (1 - percentX);
newY = bottomSide * percentY + topSide * (1 - percentY);
/* maybe use easeInOutCubic for better smoothness */
panX = lerp(-newX, mouse.x, 0.1);
panY = lerp(-newY, mouse.y, 0.1);
if (insideCircle(circle, mouse)){
circle.color = "pink";
} else {
circle.color = "blue";
}
ctx.translate(panX,panY);
/* Draw both borders only for reference */
canvasBorder.draw(ctx);
boundsBorder.drawAtCenter(ctx);
/* Draw Circle */
circle.draw(ctx);
ctx.restore();
requestAnimationFrame(update);
}
/* Events */
function mousemove(e) {
mouse = getMousePos(canvas, e);
}
/* Event Listeners */
canvas.addEventListener('mousemove', mousemove);
</script>
</body>
</html>
EDIT
I have managed to get the pan functionality to work. I just realized that all I need to do is offset by the positive and negative "widthGap"(see picture below) amount when panning. So it goes from positive "widthGap" (moves to the rght) to negative "widthGap" (moves to the left) when the "percentX" changes value like mentioned previously.
To get the smooth movement, I instead applied the lerp to the "percentX" values.
The code below is what should be replaced from the previous code above. It also shows the new variables I defined, which will make some of the variables from the previous code redundant.
UPDATED Code
var canvasBorder = new RectBorder(0,0,canvas.width,canvas.height,'red');
/* widthGap is the new leftSide formula.
* it is the gap differance between border and canvas
*/
var widthGap = (canvas.width*scaleBorderFactor - canvas.width)/2;
var heightGap = (canvas.height*scaleBorderFactor - canvas.height)/2;
var boundsBorder = new RectBorder(-widthGap,-heightGap,canvas.width+widthGap*2,canvas.height+heightGap*2,'green');
var circle = new Circle(200,200,40,'blue');
/* Draw Update */
update();
var newPercentX = 0, newPercentY = 0;
var percentX = 0, percentY = 0;
function update(){
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
width = canvas.width = canvas.clientWidth;
height = canvas.height = canvas.clientHeight;
ctx.clearRect(0,0,width,height);
ctx.save();
newPercentX = (mouse.x / width);
newPercentY = (mouse.y / height);
/* MOUSE */
percentX = lerp(percentX,newPercentX,0.05);
percentY = lerp(percentY,newPercentY,0.05);
panX = (widthGap) * percentX + (-widthGap) * (1 - percentX);
panY = (heightGap) * percentY + (-heightGap) * (1 - percentY);
ctx.translate(-panX,-panY);
if (insideCircle(circle, mouse)){
circle.color = "pink";
} else {
circle.color = "blue";
}
/* Draw both borders only for reference */
canvasBorder.draw(ctx);
boundsBorder.drawAtCenter(ctx);
/* Draw Circle */
circle.draw(ctx);
ctx.restore();
requestAnimationFrame(update);
}
Basically, I have a canvas that draws a grid. When the window changes sizes, it refreshes the grid and scene to fill the entire screen with a grid.
When I resize my browser window to a smaller size, I would like the grid and everything inside the canvas to shrink with it, while keeping the grid boxes a square shape. Websites like Agar.io use this with their canvas.
var canvas = document.getElementById("mainCanvas");
var ctx = canvas.getContext("2d");
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
function createMap() {
var gridOptions = {
majorLines: {
separation: 30,
color: '#e8e8e8'
}
};
drawGridLines(canvas, gridOptions.majorLines);
return;
}
function drawGridLines(canvas, lineOptions) {
var iWidth = canvas.width;
var iHeight = canvas.height;
ctx.strokeStyle = lineOptions.color;
ctx.strokeWidth = 1;
ctx.beginPath();
var iCount = null;
var i = null;
var x = null;
var y = null;
iCount = Math.floor(iWidth / lineOptions.separation);
for (i = 1; i <= iCount; i++) {
x = (i * lineOptions.separation);
ctx.moveTo(x, 0);
ctx.lineTo(x, iHeight);
ctx.stroke();
}
iCount = Math.floor(iHeight / lineOptions.separation);
for (i = 1; i <= iCount; i++) {
y = (i * lineOptions.separation);
ctx.moveTo(0, y);
ctx.lineTo(iWidth, y);
ctx.stroke();
}
ctx.closePath();
return;
}
function refresh() {
resize();
createMap();
}
window.onresize = function() {
refresh();
}
refresh();
body,
html {
align-content: center;
overflow: hidden;
border: 0;
margin: 0;
padding: 0;
}
#mainCanvas {
top: 0;
left: 0;
position: absolute;
overflow: hidden;
border: 0;
margin: 0;
padding: 0;
}
<canvas id="mainCanvas"></canvas>
In other words, the view of the canvas is like a bird's eye, and if there are 10 grid boxes from the center to the left side of the screen. When the window is enlarged completely, the screen would keep that same number or grid boxes from the center to the left side as it would when the browser window is resized to a smaller state. (Of course, depending on the screen ratio, it could be 10 grid boxes more or less, but roughly around there)
I tried setting the canvas size with CSS to 100% on the width and height, but that caused the grid squares to become blurry rectangles.
What can I add to cause the elements in the canvas to shrink and grow with the windows size?
Here is a sample where the elements in the canvas shrink and grow with the windows size:
<style>body{margin:0}</style>
<canvas id="mainCanvas"></canvas>
<script>
var canvas = document.getElementById("mainCanvas");
var ctx = canvas.getContext("2d");
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var gridOptions = {
majorLines: {
separation: 20,
color: '#e8e8e8'
}
};
drawGridLines(canvas, gridOptions.majorLines);
}
function drawGridLines(canvas, lineOptions) {
ctx.strokeStyle = lineOptions.color;
ctx.strokeWidth = 1;
ctx.beginPath();
var size = Math.floor(canvas.width / lineOptions.separation);
for (var i = 1; i <= lineOptions.separation; i++) {
var x = (i * size);
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
for (var i = 1; i <= Math.floor(canvas.height / size); i++) {
var y = (i * size);
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
ctx.closePath();
}
window.onresize = function() { resize(); }
resize();
</script>
Here is a live sample:
https://raw.githack.com/heldersepu/hs-scripts/master/HTML/canvasResize2.html
I already have the rectangle drawn I just need to move left to right following the mouse cursor. I know the the mouseMove and the event listener are wrong I am just leaving them there for a starting point. Here is the code:
var canvas; //This variable will be use as a reference to the canvas object
var ctx; //A variable to hold the value of the context
var rectX = 100; //rect X pos
var rectY = 200; //rect Y pos
var rectWidth = 25; //width
var rectHeight = 25; //height
var rectSpeedX = 10;
var rectSpeedY = 10;
var rectX2 = 400; //rect X pos
var rectY2 = 790; //rect Y pos
var rectWidth2 = 100; //width
var rectHeight2 = 20; //height
const WIDTH = 1000; //Width of the canvas
const HEIGHT = 800; //Height of the canvas
function mouseMove(event) {
var rectX2 = clientX;
}
document.addEventListener("mousemove", mouseMove);
window.onload = function() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext('2d');
var framesPerSecond = 30; //FPS
setInterval(function() {
drawEverything(); //Calling the rect function 30 FPS
movement();
}, 1000 / framesPerSecond); //Calls the move and draw function using an inline function. 30 FPS 1000/30
}
function drawEverything() {
ctx.fillStyle = 'red' //Draws the white background every frame covering square
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.fillStyle = 'black'
ctx.fillRect(rectX, rectY, rectWidth, rectHeight); //redraws the recntangle each frame which gives the illusion of movement
ctx.fillRect(rectX2, rectY2, rectWidth2, rectHeight2)
}
function movement() {
rectX += rectSpeedX;
rectY += rectSpeedY;
if (rectX > WIDTH - 12.5 || rectX < 0) {
rectSpeedX = -rectSpeedX;
}
if (rectY > HEIGHT - 12.5 || rectY < 0) {
rectSpeedY = -rectSpeedY;
}
}
rectX2
* {
padding: 0;
margin: 0;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
<canvas id="myCanvas" width="1000" height="800"></canvas>
You can add the following to the mouseMove function
function mouseMove(event){
rectX2 = event.pageX;
}
To get it centered on the cursor you can add:
rectX2 = event.pageX-((document.body.clientWidth-WIDTH)/2+ (rectWidth2/2));
with this you also don't need the rectX2 = MouseX at the end of your script. But if you needed it, in the handler you'd just swap out rectX2 with mouseX
var canvas; //This variable will be use as a reference to the canvas object
var ctx; //A variable to hold the value of the context
var rectX = 100;//rect X pos
var rectY = 200;//rect Y pos
var rectWidth = 25;//width
var rectHeight = 25;//height
var rectSpeedX = 10;
var rectSpeedY = 10;
var rectX2 = 400;//rect X pos
var rectY2 = 790;//rect Y pos
var rectWidth2 = 100;//width
var rectHeight2 = 20;//height
const WIDTH = 1000; //Width of the canvas
const HEIGHT = 800; //Height of the canvas
function mouseMove(event){
rectX2 = rectX2 = event.pageX;
}
document.addEventListener("mousemove", mouseMove);
window.onload = function () {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext('2d');
var framesPerSecond = 30; //FPS
setInterval(function(){
drawEverything();//Calling the rect function 30 FPS
movement();
}, 1000/framesPerSecond); //Calls the move and draw function using an inline function. 30 FPS 1000/30
}
function drawEverything(){
ctx.fillStyle = 'red' //Draws the white background every frame covering square
ctx.fillRect(0,0,WIDTH, HEIGHT);
ctx.fillStyle = 'black'
ctx.fillRect(rectX, rectY, rectWidth, rectHeight); //redraws the recntangle each frame which gives the illusion of movement
ctx.fillRect(rectX2, rectY2, rectWidth2, rectHeight2)
}
function movement(){
rectX += rectSpeedX;
rectY += rectSpeedY;
if (rectX > WIDTH-12.5|| rectX < 0){
rectSpeedX = -rectSpeedX;
}
if (rectY > HEIGHT-12.5 || rectY < 0){
rectSpeedY = -rectSpeedY;
}
}
* { padding: 0; margin: 0; }
canvas { background: #eee; display: block; margin: 0 auto; }
<canvas id="myCanvas" width="1000" height="800"></canvas>