Preventing Canvas Clear When Resizing Window

Preventing Canvas Clear when Resizing Window

I believe you have implement a listener for screen resize and redraw the canvas content when that listener fires.

Clear canvas on window resize

1. Clear size problem

var canvas = document.querySelector('.percentage-ring canvas');

var ctx = canvas.getContext('2d');

ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

You have to specify canvas size with (canvas.width, canvas.height) instead of ctx.canvas.width, ctx.canvas.height

ctx.clearRect(0, 0, canvas.width, canvas.height);

2. Interval timer usage problem

If progressRing() is called again after setInterval() without clearInterval(), e.g., due to resizing, it will continue to run with the previous interval execution surviving. This will cause the ring to be drawn twice, thrice and more.

Place var int = 0; outside functions. This initializes the int to 0 first.

And modify var int = setInterval( to int = setInterval(

Then place the following code at the beginning of progressRing()

if (int) {
clearInterval(int);
int = 0;
}

And also place int = 0; immediately after the clearInterval() call that was already there.

Consequences of canvas resizing

Setting either width or height properties of your HTMLCanvasElement will reset all the properties of your context to their default. It will also remove all clipping areas, and more interestingly in your case, it will reset the transformation matrix, and set a new pixel data (all transparent-black). And this is slow, so you are right removing it from your code.

Your call to ctx.clearRect sets the x and y coords to -2000, this means that with a default matrix transform, you are clearing non-existing pixels.

Setting your HTMLCanvasElement.height was what cleared your canvas previously.

We can also see on your second screenshot that you are drawing the grid slightly more on the bottom-right every time, this indicates that you are probably modifying the context matrix (e.g ctx.translate(x, y)).

Now that you don't reset it with the height setting, you need to do it explicitly. This can be done with ctx.setTransform(1,0,0,1,0,1).

So a basic start for a drawing function would look like

function draw() {
// clear
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform matrix (fast)
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear all pixels (fast)
//... start drawing ...

Now, there might be other properties that you assumed where reset to their defaults while they're not, e.g fillStyle etc. Most of them can be simply set to what you want when needed.

If you are using ctx.clip() however (but looking at the screenshot, it seems you're not), you'd need to save() and restore() the context's state to be able to remove the clipping area (bad API design...).

How to redraw canvas on window resize

you might need to initialize your canvas and other elemnts at window.onload.
Because at point you are initializing them, the elements on page might not be available as they've not rendered.

    var canvas , wrapperStyle, wrapper, ctx;
var startX;
var startY;
var isDown = false;
var canvasOffset, offsetX, offsetY;
window.onload = function()
{
InitCanvas();
}

function InitCanvas()
{
canvas = document.getElementById("canvasTop");
wrapper=document.getElementById('wrapper');
wrapperStyle=window.getComputedStyle(wrapper,null);
canvas.width=parseInt(wrapperStyle.getPropertyValue("width"));
canvas.height=parseInt(wrapperStyle.getPropertyValue("height"));

ctx = canvas.getContext("2d");
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.fillStyle = "skyblue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// set "erase" compositing once at start of app for better performance
ctx.globalCompositeOperation = "destination-out";

canvasOffset = $("#canvasTop").offset();
offsetX = canvasOffset.left;
offsetY = canvasOffset.top;
}

function resizeCanvas()
{
var myCanvas = document.getElementById("canvasTop");
myCanvas.width = document.documentElement.clientWidth;
myCanvas.height = document.documentElement.clientHeight;
InitCanvas();
}

Update 14-4-2017:
Call resizeCanvas on body onresize.

    <body onresize="resizeCanvas(event)" >

Update:
You might need to wire up events like this.

    <canvas id="canvasTop" width="512" height="512" onMouseDown="handleMouseDown(event)" onmousemove="handleMouseMove(event)" onMouseup="handleMouseUp(event)" onmouseout="handleMouseOut(event)"   ></canvas>

Canvas deletes it's self after resizing HTML5/JavaScript

I would change the resize handler to redraw the image after the change:

const temp = ctx.getImageData(0, 0, canvas.width, canvas.height);
canvas.width = canvasORG.clientWidth;
canvas.height = canvasORG.clientHeight;
ctx.putImageData(temp, 0, 0);

The problem with this is that if the resize is smaller than the drawing, then the portion that is now off the canvas is destroyed.

#pen {
background-color: rgba(0, 0, 0, .2);
}

#paper {
display: grid;
column-count: 1;
}

#canvasORG {
width: 97.5vw;
height: 90vh;
cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='40' height='48' viewport='0 0 100 100' style='fill:black;font-size:24px;'><text y='50%'>✍️</text></svg>") 5 27, auto;
}

#colors {
padding-top: 5px;
display: flex;
flex-wrap: wrap;
flex-direction: row;
}

#black {
width: 30px;
height: 30px;
background: black;
}

#black:hover {
background: rgba(0, 0, 0, .1);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">

<head>
<link rel="shortcut icon" type="image/icon" href="images/IYN_logo.png" />
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<script src="script.js"></script>
</head>

<body>

<div id="paper">
<div id="canvasORG">
<canvas id="canvas" style="background-color: white; border:2px solid;"></canvas>
</div>
<div id="colors">

<div id="black" onclick="color(this)"></div> 

<button id="pen" style="height: 50px; font-size: 25px; padding-bottom: 5px;">✍lt;/button>
</div>
</div>

<script>
const points = [];
const canvas = document.getElementById("canvas");
const mouse = { x: 0, y: 0, button: false }
const ctx = canvas.getContext("2d");
canvas.addEventListener("mousemove", mouseEvents);
canvas.addEventListener("mousedown", mouseEvents);
canvas.addEventListener("mouseup", mouseEvents);
var x = "black",
y = 5 * 4;
const canvasORG = document.getElementById("canvasORG");

(function () {
window.addEventListener("resize", resizeCanvas, false);
canvas.width = canvasORG.clientWidth;
canvas.height = canvasORG.clientHeight;

function resizeCanvas() {
const temp = ctx.getImageData(0, 0, canvas.width, canvas.height);
canvas.width = canvasORG.clientWidth;
canvas.height = canvasORG.clientHeight;
ctx.putImageData(temp, 0, 0);
}
resizeCanvas();
})();

function mouseEvents(e) {
mouse.x = e.pageX - 1;
mouse.y = e.pageY - 1;
const lb = mouse.button;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
if (mouse.button) {
if (!lb) { points.length = 0 }
points.push({ x: mouse.x, y: mouse.y });
drawPoints();
}
}

function drawPoints() {
$(this).scrollTop(0);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.beginPath();
if (mode == "pen") {
ctx.globalCompositeOperation = "source-over";
for (const p of points) { ctx.lineTo(p.x, p.y); }
ctx.stroke();
} else {
ctx.globalCompositeOperation = "destination-out";
ctx.arc(mouse.x, mouse.y, 8, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fill();
}
}

function color(obj) {
switch (obj.id) {
case "black":
x = "black";
break;
}
}

canvas.addEventListener("touchstart", function (e) {
mousePos = getTouchPos(canvas, e);
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mousedown", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}, false);
canvas.addEventListener("touchend", function (e) {
var mouseEvent = new MouseEvent("mouseup", {});
canvas.dispatchEvent(mouseEvent);
}, false);
canvas.addEventListener("touchmove", function (e) {
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mousemove", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}, false);

// Get the position of a touch relative to the canvas
function getTouchPos(canvasDom, touchEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: touchEvent.touches[0].clientX - rect.left,
y: touchEvent.touches[0].clientY - rect.top
};
}

document.getElementById("canvas").onwheel = function (event) {
event.preventDefault();
};

document.getElementById("canvas").onmousewheel = function (event) {
event.preventDefault();
};

canvas.addEventListener("touchstart", function (event) { event.preventDefault() })
canvas.addEventListener("touchmove", function (event) { event.preventDefault() })
canvas.addEventListener("touchend", function (event) { event.preventDefault() })
canvas.addEventListener("touchcancel", function (event) { event.preventDefault() })

var mode = "pen";
$("#pen").click(function () {
mode = "pen";
});
$("#eraser").click(function () {
mode = "eraser"
});
</script>
</body>

</html>

HTML canvas - drawing disappear on resizing

You need to redraw the scene when you resize.

setting the width or height of a canvas, even if you are setting it to the same value as before, not only clears the canvas but resets the entire canvas context. Any set properties (fillStyle, lineWidth, the clipping region, etc) will also be reset.

If you do not have the ability to redraw the scene from whatever data structures you might have representing the canvas, you can always save the entire canvas itself by drawing it to an in-memory canvas, setting the original width, and drawing the in-memory canvas back to the original canvas.

Here's a really quick example of saving the canvas bitmap and putting it back after a resize:

http://jsfiddle.net/simonsarris/weMbr/

HTML5 Canvas blocked after window resize

The problem is that you don't reset your firstRowX and secondRowX variables, so you end up drawing outside the canvas. I fixed that and also moved variable definitions where they belong.

var canvass = document.getElementById('zipper-canvas');// Could be constvar firstRowX = 0;var secondRowX = 5;// Set the correct size on loadresizecanvass();
//resize the canvass to fill browser window dynamicallywindow.addEventListener('resize', resizecanvass, false);
//for some reason i cannot draw on the canvas after it was resizedfunction resizecanvass() { canvass.width = window.innerWidth; canvass.height = 30; /** * Your drawings need to be inside this function otherwise they will be reset when * you resize the browser window and the canvass goes will be cleared. */ redraw();}

function redraw() { var ctx = canvass.getContext('2d'); ctx.clearRect(0, 0, canvass.width, canvass.height); var repetitions = canvass.width / 10; var firstRowY = canvass.height / 2 - 3; var secondRowY = canvass.height / 2 + 1; for (var r = 0; r < repetitions; r++) { ctx.beginPath(); ctx.rect(firstRowX + r*10, firstRowY, 5, 5); ctx.fillStyle = "#edeeef"; ctx.fill(); ctx.closePath(); } for (var r2 = 0; r2 < repetitions; r2++) { ctx.beginPath(); ctx.rect(secondRowX + r2*10, secondRowY, 5, 5); ctx.fillStyle = "#a2a3a5"; ctx.fill(); ctx.closePath(); }}
canvas {  outline:1px solid black;}body, html, canvas {    margin: 0px;padding:0px;    }
<canvas id='zipper-canvas'></canvas>

Resize canvas size as window is being resized (JavaScript)

You can have a callback onresize and use that to recreate the canvas:

Each time you resize the window you'll have to redraw the canvas, unless you save it somewhere.

function init() {  const canvas = document.querySelector("#sandBox");  canvas.width = window.innerWidth;  canvas.height = window.innerHeight;  const ctx = canvas.getContext("2d");
class Circle { constructor(x, y, r, c) { this.x = x; this.y = y; this.r = r; this.c = c; }
draw() { ctx.beginPath(); ctx.fillStyle = this.c; ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2); ctx.fill(); ctx.closePath(); } }
const balls = [];
for (let i = 0; i < 20; i++) { let r = Math.floor(Math.random() * 30) + 15; let x = Math.random() * (canvas.width - r * 2) + r; let y = Math.random() * (canvas.height - r * 2) + r; let c = "rgba(255,255,255,0.2)"; balls.push(new Circle(x, y, r, c)); }
balls.forEach((ball) => ball.draw());}
window.onload = function() { init();};
body {  position: relative;}
canvas { width: 100vw; height: 100vh; background-color: #393939; position: absolute; top: 0; left: 0; z-index: -1;}
<body onresize="init()">  <canvas id="sandBox"></canvas></body>

Resize canvas to viewport without loosing quality

I figured out a way:

I calculate a scaling factor, by which I scale the size, but not the position of each element drawn.
I do not want to scale the position, because then some elements might be off screen.
Thus I can not use setTransform, as suggested in the comments.
Here is the code I used for calculating the scaling factor.
If someone has improvements, please share them in the comments below!

if (window.screen.width > window.screen.height)
scalingFactor = window.screen.width * window.devicePixelRatio / 1280;
else
scalingFactor = window.screen.height * window.devicePixelRatio / 720;


Related Topics



Leave a reply



Submit