Spaces:
Running
Running
<html> | |
<head> | |
<meta viewport="width=device-width, initial-scale=1.0"> | |
<title>Layout Test</title> | |
<style> | |
html { | |
overscroll-behavior: none; | |
-webkit-user-select: none; | |
user-select: none; | |
} | |
body { | |
background: #000; | |
overflow: hidden; | |
} | |
canvas { | |
position: fixed; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
object-fit: contain; | |
} | |
#buttons { | |
position: fixed; | |
left: 50%; | |
transform: translateX(-50%); | |
display: flex; | |
} | |
button { | |
font-size: 2rem; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="canvas"></canvas> | |
<div id="buttons"> | |
<button id="toggleFixed">Fixed Res</button> | |
<button id="toggleHighDPI">High DPI</button> | |
<button id="toggleFull">Full screen</button> | |
</div> | |
<script> | |
// This is a simple test to see how to handle high DPI and fixed resolution | |
// in a way that works on all browsers, especially on mobile. | |
// We want to ensure that the canvas is always the same pixel density, and | |
// that mouse events are scaled to the canvas size. | |
var options = { | |
highDPI: true, | |
// fixedWidth: 800, | |
// fixedHeight: 600, | |
}; | |
var display = { | |
width: 10, | |
height: 10, | |
mouseX: 0, | |
mouseY: 0, | |
scale: 1, | |
fullscreen: false, | |
}; | |
function setupFullscreen() { | |
var box = canvas.parentElement; | |
function fullscreenChange(fullscreen) { | |
display.fullscreen = fullscreen; | |
setTimeout(window.onresize, 0); | |
}; | |
document.addEventListener("fullscreenchange", function(){ fullscreenChange(box === document.fullscreenElement) }); | |
function checkFullscreen() { | |
if (document.fullscreenEnabled && (box === document.fullscreenElement) !== display.fullscreen) { | |
if (display.fullscreen) box.requestFullscreen(); | |
else document.exitFullscreen(); | |
} | |
} | |
return checkFullscreen; | |
} | |
var checkFullscreen = setupFullscreen(); | |
toggleFixed.onclick = function() { | |
if (options.fixedWidth) { | |
delete options.fixedWidth; | |
delete options.fixedHeight; | |
} else { | |
options.fixedWidth = 800; | |
options.fixedHeight = 600; | |
} | |
window.onresize(); | |
}; | |
toggleHighDPI.onclick = function() { | |
if (options.highDPI) { | |
delete options.highDPI; | |
} else { | |
options.highDPI = true; | |
} | |
window.onresize(); | |
}; | |
toggleFull.onclick = function() { | |
display.fullscreen = !display.fullscreen; | |
checkFullscreen(); | |
}; | |
var ctx = canvas.getContext("2d"); | |
function draw(x, y) { | |
var w = display.width, | |
h = display.height, | |
s = display.scale; | |
ctx.save(); | |
ctx.fillStyle = "#FFF"; | |
ctx.fillRect(0, 0, w, h); | |
// draw circles | |
ctx.beginPath(); ctx.arc( 40, 40, 39, 0, 2*Math.PI); ctx.stroke(); | |
ctx.beginPath(); ctx.arc(w-40, 40, 39, 0, 2*Math.PI); ctx.stroke(); | |
ctx.beginPath(); ctx.arc(w-40, h-40, 39, 0, 2*Math.PI); ctx.stroke(); | |
ctx.beginPath(); ctx.arc( 40, h-40, 39, 0, 2*Math.PI); ctx.stroke(); | |
ctx.beginPath(); ctx.arc( w/2, h/2, Math.min(w,h)/2-2, 0, 2*Math.PI); ctx.stroke(); | |
// draw crosshairs | |
if (x !== undefined) { | |
ctx.lineWidth = 3; | |
ctx.strokeStyle = "rgba(0, 0, 0, 0.5)"; | |
ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, h); ctx.stroke(); | |
ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke(); | |
} | |
// draw resolution, scale factor and mouse position in center | |
var fontSize = 18 * s; | |
ctx.font = `${fontSize}px sans-serif`; | |
ctx.fillStyle = "black"; | |
ctx.textAlign = "center"; | |
ctx.textBaseline = "middle"; | |
var label = `${w}x${h}`; | |
if (options.fixedWidth) { | |
ctx.fillText(label, w/2, h/2); | |
} else { | |
var vw = Math.round(visualViewport.width), | |
vh = Math.round(visualViewport.height), | |
vs = visualViewport.scale.toFixed(2), | |
iw = window.innerWidth * s, | |
ih = window.innerHeight * s, | |
ow = document.documentElement.offsetWidth * s, | |
oh = document.documentElement.offsetHeight * s; | |
label = `${vw}x${vh}x${vs}@${s} = ${label}`; | |
ctx.fillText("visualViewport:", w/2, h/2 - 2*fontSize); | |
ctx.fillText(label, w/2, h/2 - fontSize); | |
ctx.fillText(`html.offsetWidth: ${ow}x${oh}`, w/2, h/2 + fontSize); | |
ctx.fillText(`window.innerWidth: ${iw}x${ih}`, w/2, h/2 + 2*fontSize); | |
} | |
ctx.restore(); | |
} | |
canvas.onmousemove = function(evt) { | |
// pageX and pageY work on touch events, too | |
var evtX = evt.pageX - this.offsetLeft, | |
evtY = evt.pageY - this.offsetTop; | |
if (options.fixedWidth) { | |
// display is centered in canvas element due to object-fit: contain | |
var ratioX = display.width / canvas.offsetWidth, | |
ratioY = display.height / canvas.offsetHeight; | |
if (ratioX > ratioY) { | |
// full width, centered vertically (letterboxed) | |
var x = evtX * ratioX; | |
var topMargin = (canvas.offsetHeight - display.height / ratioX) / 2; | |
var y = (evtY - topMargin) * ratioX; | |
} else { | |
// full height, centered horizontally (pillarboxed) | |
var y = evtY * ratioY; | |
var leftMargin = (canvas.offsetWidth - display.width / ratioY) / 2; | |
var x = (evtX - leftMargin) * ratioY; | |
} | |
} else { | |
// display fills full canvas element | |
var x = evtX * display.scale; | |
var y = evtY * display.scale; | |
} | |
// clamp to display size | |
display.mouseX = Math.max(0, Math.min(display.width, x)); | |
display.mouseY = Math.max(0, Math.min(display.height, y)); | |
draw(display.mouseX, display.mouseY); | |
}; | |
canvas.ontouchmove = function(evt) { | |
canvas.onmousemove(evt.touches[0]); | |
}; | |
window.onresize = function() { | |
const scale = window.visualViewport?.scale || 1; | |
if (options.fixedWidth) { | |
display.scale = 1; | |
display.width = options.fixedWidth; | |
display.height = options.fixedHeight; | |
} else { | |
// Safari on iOS will always report the same width, even when the device is rotated. | |
// To make our pixels stay about the same size, we use visualViewport.scale. | |
const w = Math.round((window.visualViewport?.width || window.innerWidth) * scale); | |
const h = Math.round((window.visualViewport?.height || window.innerHeight) * scale); | |
display.scale = options.highDPI ? window.devicePixelRatio : 1; | |
display.width = w * display.scale; | |
display.height = h * display.scale; | |
} | |
document.documentElement.style.fontSize = `${8 / scale}px`; // for em units | |
document.body.style.height = `${window.innerHeight - 1}px`; // prevent scroll on mobile | |
if (canvas.width != display.width || canvas.height != display.height) { | |
canvas.width = display.width; | |
canvas.height = display.height; | |
} | |
draw(); | |
} | |
window.onresize(); | |
</script> | |
</body> | |
</html> | |