Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active Sep 24, 2022
Embed
What would you like to do?
Checking ResizeObserver devicePixelContentBoxSize
body {
margin: 0;
background-color: white;
}
.outer {
margin: 0 auto;
width: 80%;
}
#compare,
canvas {
width: 100%;
height: 100px;
display: block;
}
#compare {
background-color: red;
}
.bad {
color: red;
font-weight: bold;
}
<div class="outer">
<div>using ResizeObserver (<span id="dpr-support"></span>)</div>
<canvas id="use-ResizeObserver"></canvas>
<pre id="c1"></pre>
<div>just a div</div>
<div id="compare"></div>
<pre id="c2"></pre>
<button>save canvas to png</canvas>
</div>
// Canvas Resize Method Comparison
// from https://webglfundamentals.org/webgl/webgl-resize-the-canvas-comparison.html
'use strict';
const log1Elem = document.querySelector('#c1');
const log2Elem = document.querySelector('#c2');
function setupCanvas(canvas, resizeFn) {
const gl = canvas.getContext('webgl2', {antialias: false, preserveDrawingBuffer: true});
const createShader = (gl, type, src) => {
const sh = gl.createShader(type);
gl.shaderSource(sh, src);
gl.compileShader(sh);
console.log(gl.getShaderInfoLog(sh));
return sh;
};
const prg = gl.createProgram();
gl.attachShader(prg, createShader(gl, gl.VERTEX_SHADER, `#version 300 es
void main() {
ivec2 unitQuad = ivec2(
gl_VertexID / 6 + gl_VertexID % 2,
(gl_VertexID / 2 + gl_VertexID / 3) % 2);
gl_Position = vec4(vec2(unitQuad) * 2.0 - 1.0, 0, 1);
}
`));
gl.attachShader(prg, createShader(gl, gl.FRAGMENT_SHADER, `#version 300 es
precision highp float;
uniform vec2 resolution;
out vec4 fragColor;
void main() {
vec2 odd = floor(mod(gl_FragCoord.xy, vec2(2)));
vec4 hStripe = mix(vec4(1,0,0,1), vec4(0, 1, 1, 1), odd.y);
vec4 vStripe = mix(vec4(1,0,0,1), vec4(0, 1, 1, 1), odd.x);
fragColor = mix(hStripe, vStripe, step(resolution.x / 2.0, gl_FragCoord.x));
}
`));
gl.linkProgram(prg);
gl.useProgram(prg);
const resolutionLoc = gl.getUniformLocation(prg, 'resolution');
const drawFn = (displayWidth, displayHeight) => {
canvas.width = displayWidth;
canvas.height = displayHeight;
//console.log(displayWidth, displayHeight, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.viewport(0, 0, displayWidth, displayHeight);
gl.uniform2f(resolutionLoc, displayWidth, displayHeight);
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
resizeFn(canvas, drawFn);
{
const saveBlob = (function() {
const a = document.createElement('a');
document.body.appendChild(a);
a.style.display = 'none';
return function saveData(blob, fileName) {
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
};
}());
const elem = document.querySelector('button');
elem.addEventListener('click', () => {
const canvas = gl.canvas;
canvas.toBlob((blob) => {
saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
});
});
}
}
setupCanvas(document.querySelector('#use-ResizeObserver'), (canvas, drawFn) => {
const canvasToDisplaySizeMap = new Map([[canvas, [300, 150]]]);
const resizeAndDraw = () => {
const [displayWidth, displayHeight] = canvasToDisplaySizeMap.get(canvas);
drawFn(displayWidth, displayHeight);
};
function onResize(entries) {
for (const entry of entries) {
const displayWidth = entry.devicePixelContentBoxSize[0].inlineSize;
const displayHeight = entry.devicePixelContentBoxSize[0].blockSize;
log1Elem.textContent = `canvas devicePixelContentBoxSize: ${displayWidth}, ${displayHeight}`;
canvasToDisplaySizeMap.set(entry.target, [displayWidth, displayHeight]);
resizeAndDraw();
}
}
resizeAndDraw();
const resizeObserver = new ResizeObserver(onResize);
resizeObserver.observe(canvas, {box: 'content-box'});
});
const patternSize = 4;
function createPatternDataURL() {
const ctx = document.createElement('canvas').getContext('2d');
ctx.canvas.width = patternSize;
ctx.canvas.height = patternSize;
const b = [0, 0, 0, 255];
const t = [0, 0, 0, 0];
const r = [255, 0, 0, 255];
const g = [0, 255, 0, 255];
const imageData = new ImageData(patternSize, patternSize);
imageData.data.set([
b, t, t, r,
t, b, g, t,
t, r, b, t,
g, t, t, b,
].flat());
ctx.putImageData(imageData, 0, 0);
return {patternSize, dataURL: ctx.canvas.toDataURL()};
}
/**
* Set the pattern's size on this element so that it draws where
* 1 pixel in the pattern maps to 1 devicePixel and then set
* its position so it's aligned to the pattern of the body element.
*/
{
const { patternSize, dataURL } = createPatternDataURL();
const setPattern = (elem) => {
const oneDevicePixel = 1 / devicePixelRatio;
const patternPixels = oneDevicePixel * patternSize;
elem.style.backgroundImage = `url("${dataURL}")`;
elem.style.backgroundSize = `${patternPixels}px ${patternPixels}px`;
}
const div = document.querySelector('#compare');
div.style.backgroundImage = `url("${dataURL}")`;
const resizeObserver = new ResizeObserver(entries => {
const entry = entries[0];
const displayWidth = entry.devicePixelContentBoxSize[0].inlineSize;
const displayHeight = entry.devicePixelContentBoxSize[0].blockSize;
log2Elem.textContent = `div devicePixelContentBoxSize: ${displayWidth}, ${displayHeight}`;
setPattern(div);
});
resizeObserver.observe(div, {box: 'content-box'});
}
{"name":"Checking ResizeObserver devicePixelContentBoxSize","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