简体   繁体   中英

Resize canvas to viewport without loosing quality

I am trying to have a canvas fill the entire viewport and resize, whenever the user resizes the browser window. However, when I try to use a size of 100% , it looses quality.

I am kind of a newb in JavaScript programming.

Can someone please help me?

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;

The easiest way to resize a canvas with losing the data currently on it is to store the data, then resize the canvas (which clears it), then redraw the data into the canvas. That would look something like this:

const data = ctx.getImageData( 0, 0, canvas.width, canvas.height );
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.putImageData( data, 0, 0 );

In its purest, simplest example it would look something like this:

 /* This method does the resize by storing the data and redrawing it. It loses any data outside of the screen, though. */ function onResize( event ){ const data = ctx.getImageData( 0, 0, canvas.width, canvas.height ); canvas.width = innerWidth; canvas.height = innerHeight; ctx.putImageData( data, 0, 0 ); } /* This method is for drawing some content to help visualisation. */ function onRedraw(){ const r = v => Math.floor(Math.random() * v); canvas.width = innerWidth; canvas.height = innerHeight; ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`; ctx.fillRect( 0, 0, innerWidth, innerHeight ); ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`; ctx.fillRect( 0, 0, innerWidth / 2, innerHeight / 2 ); ctx.fillRect( innerWidth / 2, innerHeight / 2, innerWidth / 2, innerHeight / 2 ); } window.addEventListener( 'resize', onResize ); const canvas = document.createElement( 'canvas' ); const ctx = canvas.getContext( '2d' ); const reset = document.getElementById( 'reset' ); document.body.appendChild( canvas ); onRedraw(); redraw.addEventListener( 'click', onRedraw );
 body { padding: 0; margin: 0; overflow: hidden; } button { position: fixed; top: 20px; left: 20px; z-index: 1; }
 <button id="redraw">Redraw</button>

However, you lose data drawn outside the canvas when your window grows and shrinks, as the data is never saved. We could hold a buffer with our graphics, which could simplify things a bit but makes the code slightly more complicated. In this case, we can use a second canvas that holds all our previously drawn maximum sized screen data, just for reference:

 /* This method does the resize by storing the data and redrawing it. It retains data outside the screen in a buffer that holds the maximum size ever drawn. */ function onResize( event ){ const data = bufferCtx.getImageData( 0, 0, bufferCanvas.width, bufferCanvas.height ); bufferCanvas.width = Math.max( bufferCanvas.width, innerWidth ); bufferCanvas.height = Math.max( bufferCanvas.height, innerHeight ); bufferCtx.putImageData( data, 0, 0 ); bufferCtx.drawImage( canvas, 0, 0 ); canvas.width = innerWidth; canvas.height = innerHeight; ctx.drawImage( bufferCanvas, 0, 0 ); } /* This method is for drawing some content to help visualisation. */ function onRedraw(){ const r = v => Math.floor(Math.random() * v); canvas.width = innerWidth; canvas.height = innerHeight; ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`; ctx.fillRect( 0, 0, innerWidth, innerHeight ); ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`; ctx.fillRect( 0, 0, innerWidth / 2, innerHeight / 2 ); ctx.fillRect( innerWidth / 2, innerHeight / 2, innerWidth / 2, innerHeight / 2 ); } window.addEventListener( 'resize', onResize ); const canvas = document.createElement( 'canvas' ); const ctx = canvas.getContext( '2d' ); const bufferCanvas = document.createElement( 'canvas' ); const bufferCtx = bufferCanvas.getContext( '2d' ); const reset = document.getElementById( 'reset' ); document.body.appendChild( canvas ); onRedraw(); redraw.addEventListener( 'click', onRedraw );
 body { padding: 0; margin: 0; overflow: hidden; } button { position: fixed; top: 20px; left: 20px; z-index: 1; }
 <button id="redraw">Redraw</button>

If you'de rather use lose no data at all and 'rescale' the canvas the best it can, perhaps best to use something like this:

 /* This method does the resize by just redrawing the canvas from a buffer: */ function onResize( event ){ bufferCanvas.width = canvas.width; bufferCanvas.height = canvas.height; bufferCtx.drawImage( canvas, 0, 0 ); canvas.width = innerWidth; canvas.height = innerHeight; ctx.drawImage( bufferCanvas, 0, 0, canvas.width, canvas.height ); } /* This method is for drawing some content to help visualisation. */ function onRedraw(){ const r = v => Math.floor(Math.random() * v); canvas.width = innerWidth; canvas.height = innerHeight; ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`; ctx.fillRect( 0, 0, innerWidth, innerHeight ); ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`; ctx.fillRect( 0, 0, innerWidth / 2, innerHeight / 2 ); ctx.fillRect( innerWidth / 2, innerHeight / 2, innerWidth / 2, innerHeight / 2 ); } window.addEventListener( 'resize', onResize ); const canvas = document.createElement( 'canvas' ); const ctx = canvas.getContext( '2d' ); const bufferCanvas = document.createElement( 'canvas' ); const bufferCtx = bufferCanvas.getContext( '2d' ); const reset = document.getElementById( 'reset' ); document.body.appendChild( canvas ); onRedraw(); redraw.addEventListener( 'click', onRedraw );
 body { padding: 0; margin: 0; overflow: hidden; } button { position: fixed; top: 20px; left: 20px; z-index: 1; }
 <button id="redraw">Redraw</button>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM