Created
March 25, 2022 14:59
-
-
Save tobyshooters/7e05535115ec6ac0eb7eb1defd1119de to your computer and use it in GitHub Desktop.
infinite.html
This file contains hidden or 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
<style> | |
body { | |
background: conic-gradient(from 90deg at 1px 1px, #fdf6e3 90deg, #eee8d5 0deg); | |
background-position: 0px 0px; | |
background-size: 40px 40px; | |
overflow: hidden; | |
} | |
.elem { | |
position: absolute; | |
display: flex; | |
background-color: #fdf6e3; | |
} | |
#add { | |
position: absolute; | |
bottom: 20; | |
right: 20; | |
} | |
#explainer { | |
position: absolute; | |
width: 400px; | |
top: 50; | |
left: 50; | |
} | |
</style> | |
<button id="add"/>Add square</button> | |
<span id="explainer"> | |
You can make an infinite canvas in vanilla Javascript by sticking everything | |
into an absolute positioned div, and then writing some mouse and wheel | |
handlers. Drag me! Pan! Don't forget to inspect the code! | |
</span> | |
<script> | |
window.onload = () => { | |
const elems = {}; | |
const makeDraggable = (id, content) => { | |
let div = document.createElement("div"); | |
div.classList.toggle("elem"); | |
div.appendChild(content); | |
div.style.top = content.style.top; | |
div.style.left = content.style.left; | |
// Store element's state in a global object; | |
elems[id] = { node: div }; | |
const mousemove = (e) => { | |
const dx = e.clientX - elems[id]._x; | |
const dy = e.clientY - elems[id]._y; | |
div.style.top = `${div.offsetTop + dy}px`; | |
div.style.left = `${div.offsetLeft + dx}px`; | |
elems[id]._x = e.clientX; | |
elems[id]._y = e.clientY; | |
} | |
const mouseup = () => { | |
document.removeEventListener('mousemove', mousemove); | |
document.removeEventListener('mouseup', mouseup); | |
} | |
div.addEventListener('mousedown', (e) => { | |
e.preventDefault(); | |
elems[id]._x = e.clientX; | |
elems[id]._y = e.clientY; | |
document.addEventListener('mousemove', mousemove); | |
document.addEventListener('mouseup', mouseup); | |
}); | |
return div; | |
} | |
document.body.addEventListener("wheel", (e) => { | |
// Move all the elements | |
Object.values(elems).forEach(elem => { | |
const child = elem.node; | |
child.style.top = `${child.offsetTop + e.deltaY}px`; | |
child.style.left = `${child.offsetLeft + e.deltaX}px`; | |
}) | |
// Move background | |
const data = (document.body.style.backgroundPosition || "0px 0px").split(" "); | |
const currX = parseInt(data[0].slice(0, -2)); | |
const currY = parseInt(data[1].slice(0, -2)); | |
document.body.style.backgroundPosition = `${currX + e.deltaX}px ${currY + e.deltaY}px`; | |
}) | |
const getRandomSquare = () => { | |
const square = document.createElement("div"); | |
square.style.backgroundColor = ["green", "silver", "salmon"][Math.floor(3 * Math.random())]; | |
square.style.width = "100px"; | |
square.style.height = "100px"; | |
square.style.top = (document.body.scrollHeight - 100) * Math.random(); | |
square.style.left = (document.body.scrollWidth - 100) * Math.random(); | |
return square; | |
} | |
// Add squares to background with button | |
document.getElementById("add").onclick = () => { | |
const id = Object.keys(elems).length; | |
const square = getRandomSquare(); | |
const draggableSquare = makeDraggable(id, square); | |
document.body.appendChild(draggableSquare); | |
} | |
const explainer = document.getElementById("explainer"); | |
const draggableExplainer = makeDraggable(0, explainer); | |
document.body.appendChild(draggableExplainer); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment