Make canvas multi-canvased; add zoom and cursor

main
kts of kettek 2024-02-10 01:41:27 -08:00
parent 963294ceaa
commit 9e07913210
1 changed files with 129 additions and 23 deletions

View File

@ -13,34 +13,72 @@
let offsetX: number let offsetX: number
let offsetY: number let offsetY: number
let zoom: number = 1.0
$: ((animation, frame, layer, img, refresh) => { let mouseX: number = 0
setTimeout(() => { let mouseY: number = 0
redraw() let mousePixelX: number = 0
}, 0) let mousePixelY: number = 0
})(animation, frame, layer, img, refresh)
let canvas: HTMLCanvasElement let rootCanvas: HTMLCanvasElement
function redraw() { let overlayCanvas: HTMLCanvasElement = document.createElement('canvas')
if (!canvas) return let canvas: HTMLCanvasElement = document.createElement('canvas')
let ctx = canvas.getContext('2d')
let overlayDirty: boolean = true
let canvasDirty: boolean = true
// check resizes canvases, etc.
function check() {
let ctx = rootCanvas.getContext('2d')
if (!ctx) return if (!ctx) return
let computedSize = getComputedStyle(canvas) let computedSize = getComputedStyle(rootCanvas)
if (canvas.width !== parseInt(computedSize.width) || canvas.height !== parseInt(computedSize.height)) { if (rootCanvas.width !== parseInt(computedSize.width) || rootCanvas.height !== parseInt(computedSize.height)) {
canvas.width = parseInt(computedSize.width) rootCanvas.width = parseInt(computedSize.width)
canvas.height = parseInt(computedSize.height) rootCanvas.height = parseInt(computedSize.height)
canvas.width = rootCanvas.width
canvas.height = rootCanvas.height
overlayCanvas.width = rootCanvas.width
overlayCanvas.height = rootCanvas.height
overlayDirty = true
canvasDirty = true
} }
if (offsetX === undefined || offsetY === undefined) { if (offsetX === undefined || offsetY === undefined) {
// Adjust offset to center image on first LOAD. // Adjust offset to center image on first LOAD.
offsetX = canvas.width/2 - img.width/2 offsetX = rootCanvas.width/2 - img.width/2
offsetY = canvas.height/2 - img.height/2 offsetY = rootCanvas.height/2 - img.height/2
} }
}
function draw() {
if (!rootCanvas) return
check()
if (canvasDirty) {
drawCanvas()
canvasDirty = false
}
if (overlayDirty) {
drawOverlay()
overlayDirty = false
}
let ctx = rootCanvas.getContext('2d')
if (!ctx) return
ctx.clearRect(0, 0, rootCanvas.width, rootCanvas.height)
ctx.drawImage(canvas, 0, 0)
ctx.drawImage(overlayCanvas, 0, 0)
}
function drawCanvas() {
let ctx = canvas.getContext('2d')
if (!ctx) return
ctx.fillStyle = '#111111' ctx.fillStyle = '#111111'
ctx.fillRect(0, 0, canvas.width, canvas.height) ctx.fillRect(0, 0, canvas.width, canvas.height)
// Draw checkboard. // Draw checkboard.
ctx.save()
ctx.imageSmoothingEnabled = false
ctx.scale(zoom, zoom)
//ctx.transform(1, 0, 0, 1, -img.width/2, -img.height/2)
{ {
ctx.beginPath() ctx.beginPath()
ctx.fillStyle = '#888888' ctx.fillStyle = '#888888'
@ -68,6 +106,28 @@
// TODO: Draw the current layer of the current frame. // TODO: Draw the current layer of the current frame.
ctx.drawImage(img, offsetX, offsetY) ctx.drawImage(img, offsetX, offsetY)
ctx.restore()
}
function drawOverlay() {
if (!overlayCanvas) return
let ctx = overlayCanvas.getContext('2d')
if (!ctx) return
ctx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height)
ctx.save()
ctx.translate(0.5, 0.5)
// Draw a cursor.
{
ctx.beginPath()
ctx.strokeStyle = '#ff0000'
ctx.lineWidth = 1
ctx.rect(offsetX*zoom+mousePixelX*zoom, offsetY*zoom+mousePixelY*zoom, 1*zoom, 1*zoom)
ctx.rect(mouseX, mouseY, 1, 1)
ctx.stroke()
}
ctx.restore()
} }
function capOffset() { function capOffset() {
@ -88,6 +148,15 @@
let x: number = 0 let x: number = 0
let y: number = 0 let y: number = 0
node.addEventListener('mouseenter', (e: MouseEvent) => {
// hide cursor
document.body.style.cursor = 'none'
})
node.addEventListener('mouseleave', (e: MouseEvent) => {
// show cursor
document.body.style.cursor = 'default'
})
node.addEventListener('mousedown', (e: MouseEvent) => { node.addEventListener('mousedown', (e: MouseEvent) => {
buttons.add(e.button) buttons.add(e.button)
x = e.clientX x = e.clientX
@ -95,6 +164,21 @@
}) })
node.addEventListener('wheel', (e: WheelEvent) => { node.addEventListener('wheel', (e: WheelEvent) => {
if (e.ctrlKey) {
if (e.deltaY < 0) {
zoom *= 2
} else if (e.deltaY > 0) {
zoom /= 2
}
if (zoom > 1) {
zoom = Math.round(zoom)
}
capOffset()
canvasDirty = true
overlayDirty = true
return
}
if (e.deltaY < 0) { if (e.deltaY < 0) {
if (e.shiftKey) { if (e.shiftKey) {
offsetX-- offsetX--
@ -109,10 +193,22 @@
} }
} }
capOffset() capOffset()
redraw() canvasDirty = true
overlayDirty = true
}) })
window.addEventListener('mousemove', (e: MouseEvent) => { window.addEventListener('mousemove', (e: MouseEvent) => {
if (!canvas) return
// Get mouse position relative to canvas.
{
let rect = canvas.getBoundingClientRect()
mouseX = e.offsetX - rect.left
mouseY = e.offsetY - rect.top
mousePixelX = Math.floor(mouseX / zoom - offsetX)
mousePixelY = Math.floor(mouseY / zoom - offsetY)
overlayDirty = true
}
if (buttons.size === 0) return if (buttons.size === 0) return
let dx = e.clientX - x let dx = e.clientX - x
let dy = e.clientY - y let dy = e.clientY - y
@ -123,14 +219,15 @@
console.log('0') console.log('0')
} }
if (buttons.has(1)) { if (buttons.has(1)) {
offsetX += dx offsetX += dx / zoom
offsetY += dy offsetY += dy / zoom
capOffset() capOffset()
canvasDirty = true
overlayDirty = true
} }
if (buttons.has(2)) { if (buttons.has(2)) {
console.log('2') console.log('2')
} }
redraw()
}) })
window.addEventListener('mouseup', (e: MouseEvent) => { window.addEventListener('mouseup', (e: MouseEvent) => {
@ -139,12 +236,20 @@
} }
onMount(() => { onMount(() => {
redraw() let frameID: number = 0
let frameDraw = () => {
draw()
frameID = window.requestAnimationFrame(frameDraw)
}
frameID = window.requestAnimationFrame(frameDraw)
return () => {
window.cancelAnimationFrame(frameID)
}
}) })
</script> </script>
<main> <main>
<canvas bind:this={canvas} use:canvasMousedown on:contextmenu={(e)=>e.preventDefault()}></canvas> <canvas bind:this={rootCanvas} use:canvasMousedown on:contextmenu={(e)=>e.preventDefault()}></canvas>
</main> </main>
<style> <style>
@ -156,5 +261,6 @@
canvas { canvas {
width: 100%; width: 100%;
height: 100%; height: 100%;
image-rendering: pixelated;
} }
</style> </style>