Last active
September 23, 2022 20:17
-
-
Save greggman/018b43801cf87e75eb52e1e446dd7a6f to your computer and use it in GitHub Desktop.
Canvas Resize Method Comparison (via putImageData)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin: 0; | |
background-color: white; | |
} | |
.outer { | |
margin: 0 auto; | |
width: 80%; | |
} | |
canvas { | |
width: 100%; | |
height: 100px; | |
display: block; | |
} | |
.bad { | |
color: red; | |
font-weight: bold; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="outer"> | |
<div>using round(clientWidth * devicePixelRatio)</div> | |
<canvas id="use-clientWidth"></canvas> | |
<div>using round(getBoundingClientRect().width * devicePixelRatio)</div> | |
<canvas id="use-getBoundingClientRect"></canvas> | |
<div>using ResizeObserver (<span id="dpr-support"></span>)</div> | |
<canvas id="use-ResizeObserver"></canvas> | |
<div id="warning" style="color: red; display: none">(warning: no support for device-pixel-content-box)</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Canvas Resize Method Comparison | |
// from https://webglfundamentals.org/webgl/webgl-resize-the-canvas-comparison.html | |
'use strict'; | |
function setupCanvas(canvas, resizeFn) { | |
const ctx = canvas.getContext('2d'); | |
const drawFn = (displayWidth, displayHeight) => { | |
canvas.width = displayWidth; | |
canvas.height = displayHeight; | |
const imgData = ctx.createImageData(displayWidth, displayHeight); | |
const u32View = new Uint32Array(imgData.data.buffer); | |
u32View.fill(0xFFFFFFFF); | |
for (let y = 0; y < displayHeight; y += 2) { | |
for (let x = 0; x < displayWidth / 2; ++x) { | |
u32View[y * displayWidth + x] = 0xFF000000; | |
} | |
} | |
for (let x = displayWidth / 2 | 0; x < displayWidth; x += 2) { | |
for (let y = 0; y < displayHeight; ++y) { | |
u32View[y * displayWidth + x] = 0xFF000000; | |
} | |
} | |
ctx.putImageData(imgData, 0, 0); | |
}; | |
resizeFn(canvas, drawFn); | |
} | |
setupCanvas(document.querySelector('#use-clientWidth'), (canvas, drawFn) => { | |
const resizeAndDraw = () => { | |
const dpr = window.devicePixelRatio; | |
const displayWidth = Math.round(canvas.clientWidth * dpr); | |
const displayHeight = Math.round(canvas.clientHeight * dpr); | |
drawFn(displayWidth, displayHeight); | |
}; | |
resizeAndDraw(); | |
window.addEventListener('resize', resizeAndDraw); | |
}); | |
setupCanvas(document.querySelector('#use-getBoundingClientRect'), (canvas, drawFn) => { | |
const resizeAndDraw = () => { | |
const dpr = window.devicePixelRatio; | |
const {width, height} = canvas.getBoundingClientRect(); | |
const displayWidth = Math.round(width * dpr); | |
const displayHeight = Math.round(height * dpr); | |
drawFn(displayWidth, displayHeight); | |
}; | |
resizeAndDraw(); | |
window.addEventListener('resize', resizeAndDraw); | |
}); | |
setupCanvas(document.querySelector('#use-ResizeObserver'), (canvas, drawFn) => { | |
const canvasToDisplaySizeMap = new Map([[canvas, [300, 150]]]); | |
const dprSupportElem = document.querySelector('#dpr-support'); | |
const resizeAndDraw = () => { | |
const [displayWidth, displayHeight] = canvasToDisplaySizeMap.get(canvas); | |
drawFn(displayWidth, displayHeight); | |
}; | |
function onResize(entries) { | |
for (const entry of entries) { | |
let width; | |
let height; | |
let dpr = window.devicePixelRatio; | |
let dprSupport = false; | |
if (entry.devicePixelContentBoxSize) { | |
// NOTE: Only this path gives the correct answer | |
// The other paths are an imperfect fallback | |
// for browsers that don't provide anyway to do this | |
width = entry.devicePixelContentBoxSize[0].inlineSize; | |
height = entry.devicePixelContentBoxSize[0].blockSize; | |
dpr = 1; // it's already in width and height | |
dprSupport = true; | |
} else if (entry.contentBoxSize) { | |
if (entry.contentBoxSize[0]) { | |
width = entry.contentBoxSize[0].inlineSize; | |
height = entry.contentBoxSize[0].blockSize; | |
} else { | |
// legacy | |
width = entry.contentBoxSize.inlineSize; | |
height = entry.contentBoxSize.blockSize; | |
} | |
} else { | |
// legacy | |
width = entry.contentRect.width; | |
height = entry.contentRect.height; | |
} | |
const displayWidth = Math.round(width * dpr); | |
const displayHeight = Math.round(height * dpr); | |
canvasToDisplaySizeMap.set(entry.target, [displayWidth, displayHeight]); | |
resizeAndDraw(); | |
dprSupportElem.textContent = dprSupport ? "dpr supported 😀" : "dpr not supported 😢"; | |
dprSupportElem.classList.toggle('bad', !dprSupport); | |
} | |
} | |
resizeAndDraw(); | |
const resizeObserver = new ResizeObserver(onResize); | |
resizeObserver.observe(canvas, {box: 'content-box'}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"name":"Canvas Resize Method Comparison (via putImageData)","settings":{},"filenames":["index.html","index.css","index.js"]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment