148 lines
No EOL
4.1 KiB
JavaScript
148 lines
No EOL
4.1 KiB
JavaScript
import { canvas, resize, cursor, brushSize, setBrushSize } from './state.js';
|
|
import { setupUI, handleInput, setLastPos, updateBrushUI } from './ui.js';
|
|
import { update } from './physics.js';
|
|
import { draw } from './renderer.js';
|
|
import { loadSettings, config, saveSettings } from './config.js';
|
|
|
|
let updateAccumulator = 0;
|
|
|
|
export function stepOnce() {
|
|
update();
|
|
draw();
|
|
}
|
|
|
|
function init() {
|
|
loadSettings();
|
|
if (config.brush?.size) {
|
|
setBrushSize(config.brush.size);
|
|
}
|
|
resize();
|
|
window.addEventListener('resize', resize);
|
|
setupUI();
|
|
|
|
// Input Handling
|
|
let isMouseDown = false;
|
|
let isRightClick = false;
|
|
|
|
canvas.addEventListener('contextmenu', (e) => e.preventDefault());
|
|
|
|
canvas.addEventListener('mousedown', (e) => {
|
|
isMouseDown = true;
|
|
isRightClick = (e.button === 2);
|
|
cursor.visible = true;
|
|
cursor.x = e.clientX;
|
|
cursor.y = e.clientY;
|
|
handleInput(e, isRightClick ? 'eraser' : null);
|
|
});
|
|
|
|
window.addEventListener('mouseup', () => {
|
|
isMouseDown = false;
|
|
isRightClick = false;
|
|
setLastPos(null);
|
|
});
|
|
|
|
canvas.addEventListener('mousemove', (e) => {
|
|
cursor.visible = true;
|
|
cursor.x = e.clientX;
|
|
cursor.y = e.clientY;
|
|
if (isMouseDown) {
|
|
handleInput(e, isRightClick ? 'eraser' : null);
|
|
}
|
|
});
|
|
|
|
canvas.addEventListener('mouseenter', () => {
|
|
cursor.visible = true;
|
|
});
|
|
|
|
canvas.addEventListener('mouseleave', () => {
|
|
cursor.visible = false;
|
|
setLastPos(null);
|
|
});
|
|
|
|
// Mouse wheel to change brush size
|
|
canvas.addEventListener('wheel', (e) => {
|
|
e.preventDefault();
|
|
const delta = e.deltaY < 0 ? 1 : -1;
|
|
setBrushSize(brushSize + delta);
|
|
config.brush.size = brushSize;
|
|
saveSettings();
|
|
updateBrushUI();
|
|
}, { passive: false });
|
|
|
|
// Touch Support
|
|
canvas.addEventListener('touchstart', (e) => {
|
|
isMouseDown = true;
|
|
if (e.touches.length > 0) {
|
|
cursor.visible = true;
|
|
cursor.x = e.touches[0].clientX;
|
|
cursor.y = e.touches[0].clientY;
|
|
}
|
|
handleInput(e);
|
|
e.preventDefault();
|
|
}, { passive: false });
|
|
|
|
canvas.addEventListener('touchend', (e) => {
|
|
if (e.touches.length === 0) {
|
|
isMouseDown = false;
|
|
cursor.visible = false;
|
|
setLastPos(null);
|
|
}
|
|
});
|
|
|
|
canvas.addEventListener('touchmove', (e) => {
|
|
if (e.touches.length > 0) {
|
|
cursor.visible = true;
|
|
cursor.x = e.touches[0].clientX;
|
|
cursor.y = e.touches[0].clientY;
|
|
}
|
|
if (isMouseDown) handleInput(e);
|
|
e.preventDefault();
|
|
}, { passive: false });
|
|
|
|
// Keyboard Shortcuts
|
|
window.addEventListener('keydown', (e) => {
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
|
|
|
if (e.code === 'Space') {
|
|
e.preventDefault();
|
|
config.time.paused = !config.time.paused;
|
|
saveSettings();
|
|
const pauseBtn = document.getElementById('togglePause');
|
|
if (pauseBtn) pauseBtn.textContent = config.time.paused ? '▶' : '⏸';
|
|
} else if (e.code === 'KeyS' || e.code === 'Period') {
|
|
if (config.time.paused) {
|
|
stepOnce();
|
|
}
|
|
} else if (e.code === 'BracketLeft') {
|
|
setBrushSize(brushSize - 1);
|
|
config.brush.size = brushSize;
|
|
saveSettings();
|
|
updateBrushUI();
|
|
} else if (e.code === 'BracketRight') {
|
|
setBrushSize(brushSize + 1);
|
|
config.brush.size = brushSize;
|
|
saveSettings();
|
|
updateBrushUI();
|
|
}
|
|
});
|
|
|
|
gameLoop();
|
|
}
|
|
|
|
function gameLoop() {
|
|
if (!config.time.paused) {
|
|
updateAccumulator += config.time.speed;
|
|
while (updateAccumulator >= 1) {
|
|
update();
|
|
updateAccumulator -= 1;
|
|
if (updateAccumulator > 30) {
|
|
updateAccumulator = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
draw();
|
|
requestAnimationFrame(gameLoop);
|
|
}
|
|
|
|
init(); |