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 offsetY: number
let zoom: number = 1.0
$: ((animation, frame, layer, img, refresh) => {
setTimeout(() => {
redraw()
}, 0)
})(animation, frame, layer, img, refresh)
let canvas: HTMLCanvasElement
function redraw() {
if (!canvas) return
let ctx = canvas.getContext('2d')
let mouseX: number = 0
let mouseY: number = 0
let mousePixelX: number = 0
let mousePixelY: number = 0
let rootCanvas: HTMLCanvasElement
let overlayCanvas: HTMLCanvasElement = document.createElement('canvas')
let canvas: HTMLCanvasElement = document.createElement('canvas')
let overlayDirty: boolean = true
let canvasDirty: boolean = true
// check resizes canvases, etc.
function check() {
let ctx = rootCanvas.getContext('2d')
if (!ctx) return
let computedSize = getComputedStyle(canvas)
if (canvas.width !== parseInt(computedSize.width) || canvas.height !== parseInt(computedSize.height)) {
canvas.width = parseInt(computedSize.width)
canvas.height = parseInt(computedSize.height)
let computedSize = getComputedStyle(rootCanvas)
if (rootCanvas.width !== parseInt(computedSize.width) || rootCanvas.height !== parseInt(computedSize.height)) {
rootCanvas.width = parseInt(computedSize.width)
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) {
// Adjust offset to center image on first LOAD.
offsetX = canvas.width/2 - img.width/2
offsetY = canvas.height/2 - img.height/2
offsetX = rootCanvas.width/2 - img.width/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.fillRect(0, 0, canvas.width, canvas.height)
// 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.fillStyle = '#888888'
@ -68,6 +106,28 @@
// TODO: Draw the current layer of the current frame.
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() {
@ -88,6 +148,15 @@
let x: 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) => {
buttons.add(e.button)
x = e.clientX
@ -95,6 +164,21 @@
})
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.shiftKey) {
offsetX--
@ -109,10 +193,22 @@
}
}
capOffset()
redraw()
canvasDirty = true
overlayDirty = true
})
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
let dx = e.clientX - x
let dy = e.clientY - y
@ -123,14 +219,15 @@
console.log('0')
}
if (buttons.has(1)) {
offsetX += dx
offsetY += dy
offsetX += dx / zoom
offsetY += dy / zoom
capOffset()
canvasDirty = true
overlayDirty = true
}
if (buttons.has(2)) {
console.log('2')
}
redraw()
})
window.addEventListener('mouseup', (e: MouseEvent) => {
@ -139,12 +236,20 @@
}
onMount(() => {
redraw()
let frameID: number = 0
let frameDraw = () => {
draw()
frameID = window.requestAnimationFrame(frameDraw)
}
frameID = window.requestAnimationFrame(frameDraw)
return () => {
window.cancelAnimationFrame(frameID)
}
})
</script>
<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>
<style>
@ -156,5 +261,6 @@
canvas {
width: 100%;
height: 100%;
image-rendering: pixelated;
}
</style>