diff --git a/frontend/src/sections/Editor2D.svelte b/frontend/src/sections/Editor2D.svelte
index 0a2a8c8..59d9f06 100644
--- a/frontend/src/sections/Editor2D.svelte
+++ b/frontend/src/sections/Editor2D.svelte
@@ -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)
+ }
})
-
+
\ No newline at end of file