Last active
March 26, 2023 19:05
-
-
Save HMilbradt/84cf24e735f99da8115ba523bbc1def2 to your computer and use it in GitHub Desktop.
Zooming and panning natively in HTML Canvas.
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
<html> | |
<body> | |
<canvas width="500" height="500" id="canvas"></canvas> | |
<script> | |
// Read here: https://harrisonmilbradt.com/articles/canvas-panning-and-zooming | |
const canvas = document.getElementById('canvas') | |
const ctx = canvas.getContext('2d') | |
const viewportTransform = { | |
x: 0, | |
y: 0, | |
scale: 1 | |
} | |
// From here on, everything we'll write will go below 👇 | |
const drawRect = (x, y, width, height, color) => { | |
ctx.fillStyle = color | |
ctx.fillRect(x, y, width, height) | |
} | |
const render = () => { | |
// New code 👇 | |
ctx.setTransform(1, 0, 0, 1, 0, 0); | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
ctx.setTransform(viewportTransform.scale, 0, 0, viewportTransform.scale, viewportTransform.x, viewportTransform.y); | |
// New Code 👆 | |
drawRect(0, 0, 100, 100, 'red'); | |
drawRect(200, 200, 100, 100, 'blue'); | |
} | |
// We need to keep track of our previous mouse position for later | |
let previousX = 0, previousY = 0; | |
const updatePanning = (e) => { | |
const localX = e.clientX; | |
const localY = e.clientY; | |
viewportTransform.x += localX - previousX; | |
viewportTransform.y += localY - previousY; | |
previousX = localX; | |
previousY = localY; | |
} | |
const updateZooming = (e) => { | |
const oldScale = viewportTransform.scale; | |
const oldX = viewportTransform.x; | |
const oldY = viewportTransform.y; | |
const localX = e.clientX; | |
const localY = e.clientY; | |
const previousScale = viewportTransform.scale; | |
const newScale = viewportTransform.scale += e.deltaY * -0.01; | |
const newX = localX - (localX - oldX) * (newScale / previousScale); | |
const newY = localY - (localY - oldY) * (newScale / previousScale); | |
viewportTransform.x = newX; | |
viewportTransform.y = newY; | |
viewportTransform.scale = newScale; | |
} | |
const onMouseMove = (e) => { | |
updatePanning(e) | |
render() | |
console.log(e) | |
} | |
const onMouseWheel = (e) => { | |
updateZooming(e) | |
render() | |
console.log(e) | |
} | |
canvas.addEventListener("wheel", onMouseWheel); | |
canvas.addEventListener("mousedown", (e) => { | |
previousX = e.clientX; | |
previousY = e.clientY; | |
canvas.addEventListener("mousemove", onMouseMove); | |
}) | |
canvas.addEventListener("mouseup", (e) => { | |
canvas.removeEventListener("mousemove", onMouseMove); | |
}) | |
render() | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment