drawing to responsive canvas that is 100% width and height - javascript

I am trying to get a shape to follow the mouse cursor on a large canvas. This code isn't working as expected. How can I modify this to the relative canvas size if the canvas is 100% width / height of the screen?
Also, I am aware of requestanimationframe, but for the purpose of demonstrating this issue, please ignore it (I'm using it but its a complicated mess of timings that aren't related to this example).
Fiddle: https://jsfiddle.net/gs5a4z84/
CSS:
* { margin: 0; padding: 0;}
body, html { height:100%; }
canvas {
image-rendering: -moz-crisp-edges; /* Firefox */
image-rendering: pixelated; /* Chrome */
position:absolute;
width:100%;
height:100%;
}
HTML/JS:
<!DOCTYPE html>
<html>
<head>
<link href="main.css" rel="stylesheet"</link>
</head>
<body>
<div id="wrapper">
<canvas id="ctx" width="256" height="144"></canvas>
</div>
<script>
var mouseX, mouseY;
const WIDTH = 256;
const HEIGHT = 144;
var ctx = document.getElementById('ctx').getContext('2d');
ctx.canvas.addEventListener('mousemove', setMousePosition, false);
function setMousePosition(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
function drawCursor() {
ctx.clearRect(0,0,WIDTH,HEIGHT);
ctx.fillStyle = "#FF6A6A";
ctx.fillRect(mouseX, mouseY, 16, 16);
}
setInterval(function (){
drawCursor();
}, 40);
</script>
</body>
</html>
Thank you for any and all help.

If you use CSS width and height property for the canvas object, then you won't actually change how many pixels there are on the canvas, but how big it is. To fix your problem, set the canvas' width and height property using JavaScript:
JavaScript:
ctx.canvas.width = innerWidth;
ctx.canvas.height = innerHeight;
CSS:
body, canvas {
margin: 0;
padding: 0;
}

Related

Responsive PaperJS canvas (stretch canvas)

PaperJS has documentation how to setup the canvas in to resize the canvas when the window changes dimensions. The same solution can be found in this SO answer.
The problem with this approach is that it will change the coordinates while drawing, and also it will be resizing the canvas, which can be less performant.
I would like to scale the canvas without changing the width of the element. The problem I found is that PaperJS adds a width and height to the canvas element in runtime, and I could not find a way to stop this behaviour, this will result the calculated value when the setup is done, and could be for instance:
width="817" height="817"
Here is an example of a HTML Canvas that behaves the way I intend, and also the PaperJS version which does not scale. Resize the browser window to see how the example in the bottom will stretch the canvas content, but keeping the circle in the center of the canvas.
I would like to find a way to configure PaperJS that would work in the same way, without having to change the draw coordinates inside a onResize handler.
Is this possible?
const SIZE = 150;
window.onload = () => {
// --- Paperjs ---
const canvas = document.getElementById('paper');
paper.setup(canvas);
// draw circle in the center of the view
const c = new paper.Path.Circle(new paper.Point(SIZE, SIZE), SIZE);
c.strokeColor = 'white';
c.strokeWidth = 3;
// --- END Paperjs ---
// --- Canvas ---
const ctx = document.querySelector("#canvas").getContext("2d");
ctx.strokeStyle = "#FFFFFF";
ctx.fillStyle = "white";
ctx.lineWidth = 3;
ctx.beginPath();
// x, y, width
ctx.arc(SIZE, SIZE, SIZE, 0, 2 * Math.PI);
ctx.stroke();
// --- END Canvas ---
}
.wrapper {
margin-left: 10vw;
width: 80vw;
border: 1px solid cyan;
}
.responsive-canvas {
background-color: silver;
max-width: inherit;
max-height: inherit;
height: inherit !important;
width: inherit !important;
object-fit: contain;
}
* {
margin: 0;
padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.17/paper-full.min.js"></script>
<h2>PaperJS</h2>
<div class="wrapper">
<canvas id="paper" class="responsive-canvas" width="300px" height="300px"></canvas>
</div>
<h2>Canvas</h2>
<div class="wrapper">
<canvas class="responsive-canvas" id="canvas" width="300px" height="300px"></canvas>
</div>

Why am I getting overflow when i mentioned width to be window.innerwidth and height to be window.innerheight

screenshot for reference
So I have been wondering what is causing the overflow to happen when I mention height and width to be window.innerheight and window.innerwidth respectively.
Here's a snippet:
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
}
canvas {
border: 1px solid;
}
<body>
<canvas id="canvas" width=""></canvas>
<script src="./canvas.js"></script>
</body>
Set the width and height in css by using:
canvas {
width: 100%;
height: 100%;
}
The short answer seems to be that width and height are HTML attributes - nothing to do with CSS - whereas box-sizing is to do with CSS. i.e. the box-sizing is not affecting the width/height + border settings so you get overflow.
For the longer answer I tried to understand what was happening on my Windows10 system (Edge/Chrome) when setting the canvas dimensions by CSS, leaving the attributes untouched. I only partially understand, the x direction seemed fine, the border was there but the vertical border on the right was hidden bizy a vertical scroll bar - it seemed as though the calculation was one pixel out until I went full screen at which point there was no scrolling.
Hopefully someone else can explain in more depth what is happening here as the box-sizing on the canvas seemed to work but not to the full extent.
This snippet sets everything to have margin and padding 0 just to be on the safe side.
canvas = document.querySelector('canvas');
function resize() {
//canvas.width = window.innerWidth;
//canvas.height = window.innerHeight;
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';
}
window.onresize = resize;
resize();
* {
box-sizing: border-box;
}
* {
margin: 0;
padding: 0;
}
canvas {
border: 10px solid;
}
<canvas id="canvas" width=""></canvas>

How to make canvas full width and height of body while not stretching or blurring drawings?

I want to make a canvas that occupies the full width and height of the body (with no scrolling), and has non stretched and non blurred drawings. Whenever I make a canvas 100vw and 100vh, the drawings get blurred and stretched image, with incorrect sizings.
Example:
<html>
<body>
<canvas id="c" style="border: 2px solid black;"></canvas>
<style> body, html {padding: 0; margin: 0; overflow: hidden;} </style>
<script>
var c = document.getElementById("c");
var ctx = c.getContext("2d");
function draw() {
ctx.fillRect(100, 50, 100, 75);
c.style.width = document.body.clientWidth;
c.style.height = document.body.clientHeight;
}
draw();
</script>
This code produces a black rectangle, but very blurry and a much larger size then I would like and expect (considering the width and height of the canvas is the whole body, and the width and height of the rectangle is only 100 and 75). Are there any fixes to this issue?
Thanks.
The reason why it gets stretched etc. is because of several reasons:
The rectangle must be drawn after the size of the canvas is set
When setting the canvas size, you don't actually set the style height and width, since the element canvas already has those attributes itself.
Also note that you will want your body/html css height to be 100% since it begins as auto, when canvas' height is set to the height of the body, it only fills up the little space body initially gives it compared to the whole screen.
Your new draw() function should look like this
function draw() {
c.width = document.body.clientWidth; // Remove .style from both
c.height = document.body.clientHeight;
ctx.fillRect(100, 50, 100, 75); // fillRect post setting size
}
and css
<style> body, html {padding: 0; margin: 0; overflow: hidden; height:100%;} </style>

How to rerender (rather than resample) a canvas?

Chrome and Firefox handle the following code fine when rendered at 100%.
<!DOCTYPE html>
<html>
<head>
<script type="application/x-javascript">
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(50,50,25,0,2*Math.PI);
ctx.stroke();
}
}
</script>
</head>
<body onload="draw();">
<canvas id="canvas" width="100" height="100">
<p>This example requires a browser that supports the
HTML5
<canvas> feature.</p>
</canvas>
</body>
</html>
But if someone magnifies my page a little, the canvas is sampled, not repainted. It's only a little ugly at 150%, but by the time the viewer reaches 300%, it will look very ugly:
How do I rewrite the code above so that the circle is repainted at the new magnification, not resampled?
This answer leads me to believe that it can be easily done. My attempt
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
html, body {
width: 100%;
height: 100%;
margin: 0px;
}
</style>
<script type="application/x-javascript">
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
ctx.beginPath();
ctx.arc(ctx.canvas.width/2, ctx.canvas.height/2,
ctx.canvas.height/4,0,2*Math.PI);
ctx.stroke();
}
}
</script>
</head>
<body onload="draw();">
<canvas id="canvas">
<p>This example requires a browser that supports the
HTML5
<canvas> feature.</p>
</canvas>
</body>
</html>
is no good. The circle is still resampled when the user magnifies. Can you do better?
(Chrome and Firefox behave differently when one magnifies. The effect I'm aiming for is for the circle to stay put with its center in the middle of the canvas and its radius a quarter of the window's height.)
Try doing the onload combined with the window resize event to redraw the canvas (zooming triggers the resize event):
window.addEventListener('resize', draw);
CANVAS REDRAWING
Example: http://jsfiddle.net/ksvyndwp/
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
ctx.beginPath();
ctx.arc(ctx.canvas.width/2, ctx.canvas.height/2,
ctx.canvas.height/4,0,2*Math.PI);
ctx.stroke();
}
}
draw();
window.addEventListener('resize', draw);
html, body {
width: 100%;
height: 100%;
margin: 0px;
}
<canvas id="canvas">
<p>This example requires a browser that supports the
HTML5
<canvas> feature.</p>
</canvas>
If you're still worried about pixelization at higher zoom levels, I'm afraid there's nothing you can do about it directly on the canvas, because canvas' pixels scale with the window, and although you enter the sub-pixel rendering stage, there's nothing you can do to make it look better.
CSS SCALING
However, if you don't want to do it purely inside the canvas, you can do a simple CSS transformation that will make your circle look correctly on all zoom levels. Setting the width and the height to 100% will keep the canvas (as in canvas, the DOM element; not the actual canvas.width and canvas.height) the size of the window, so it's basically "immune" to zooming.
See it here (actually works much better when zooming the jsfiddle: http://jsfiddle.net/406h7xm0/ than the Stack Overflow iframe):
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
ctx.beginPath();
ctx.arc(ctx.canvas.width/2, ctx.canvas.height/2,
ctx.canvas.height/4,0,2*Math.PI);
ctx.stroke();
}
console.log(window.innerWidth);
}
draw();
html, body {
width: 100%;
height: 100%;
margin: 0px;
}
canvas {
width: 100%;
height: 100%;
}
<canvas id="canvas"></canvas>
The final solution of what you're trying to do probably lies somewhere between the two approaches I've shown you.

Why putting HTML5 canvas into the background doesn't work?

I am trying to put an HTML5 canvas as the background of my web page. I can't get it to work no matter what I do. Does anyone know how? This is my current attempt:
<!DOCTYPE html>
<html>
<head> <title> Test </title> </head>
<style>
#bgcanvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index = -1;
}
</style>
<script type="text/javascript">
function fillCanvas(){
var bg = document.getElementById("bgcanvas");
var ctx = bg.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 50, 50);
ctx = document.getElementById("screen").getContext("2d");
ctx.fillStyle = "#0000FF";
ctx.fillRect(0, 0, 500, 400);
}
</script>
<body onload="fillCanvas();">
<center>
<h1>Test Page</h1>
<canvas id="screen" width=720 height=480 style="background: black;">
Your browser sucks. Please upgrade.
</canvas>
</center>
</body>
<canvas id="bgcanvas" width=100 height=100> </canvas>
</html>
Thanks!
There are a few simple errors here you can adjust to make it work as pointed out in the various comments:
#bgcanvas {
/* ... rest not shown ... */
/* remove these unless you want the content to scale like an image:
width: 100%;
height: 100%;
*/
z-index: -1; /* change = to :. = is not supported in CSS for properties */
}
Move the canvas element inside the body tags:
...
<canvas id="bgcanvas" width=100 height=100> </canvas>
</body>
Tip: Increase the size to get better quality (you would currently scaling an "image" of 100x100 pixels to about the size of the window which will can give a poor result).
You can alternatively drop the CSS sizing and set the canvas size this way in your script before you draw anything to it:
var bg = document.getElementById("bgcanvas");
bg.width = window.innerWidth;
bg.height = window.innerHeight;

Categories

Resources