1.3.2
by Sharxxy
HTML
<div id="toolbar">
<div id="toolbarContent">
<img src="https://via.placeholder.com/120x30?text=AquaMotion" alt="AquaMotion Logo" id="logo">
<button data-tool="pencil" class="active">✏️ Pencil</button>
<button data-tool="eraser">🩹 Eraser</button>
<button data-tool="line">➖ Line</button>
<button data-tool="rect">⬛ Rect</button>
<button data-tool="circle">⭕ Circle</button>
<button data-tool="bucket">🪣 Bucket</button>
<button data-tool="path">🖊 Path</button>
<button data-tool="text">🔤 Text</button>
Color: <input type="color" id="colorPicker" value="#00bfff">
Size: <input type="range" id="brushSize" min="1" max="50" value="5">
<button id="undo">↩️ Undo</button>
<button id="redo">↪️ Redo</button>
<button id="zoomIn">🔍➕</button>
<button id="zoomOut">🔍➖</button>
<button id="playPause">▶️ Play</button>
FPS: <input type="number" id="fps" value="12" min="1" max="60">
W: <input type="number" id="canvasWidth" value="800" min="100" max="2000">
H: <input type="number" id="canvasHeight" value="600" min="100" max="2000">
<button id="exportAqua">💾 .aqua</button>
<button id="exportGIF">🎞 GIF</button>
</div>
</div>
CSS
#toolbar {
position: fixed;
top: 0;
left: 0;
right: 0;
background: linear-gradient(90deg, #021B38, #03396c);
padding: 8px;
border-bottom: 2px solid #00bfff;
z-index: 1000;
overflow-x: auto; /* scroll if it’s too wide */
white-space: nowrap;
}
#toolbarContent {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: nowrap;
}
#toolbar button, #toolbar input {
background: #0a2a4d;
border: 1px solid #00bfff;
color: #fff;
padding: 6px 10px;
border-radius: 6px;
cursor: pointer;
}
#toolbar button.active {
background: #00bfff;
color: #021B38;
}
JavaScript
// AquaMotion — big rewrite with fixes & extras
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let state = {
tool: 'pencil',
color: '#00bfff',
size: 6,
brushStyle: 'round',
pathCursor: false,
zoom: 1,
pan: {x:0,y:0},
draggingPan: false,
spaceDown: false
};
// data model: frames -> layers -> strokes, audios
let frames = [{ layers: [ { name: 'Layer 1', visible: true, opacity:1, strokes:[], bgColor: null } ], audios: [] }];
let currentFrame = 0;
let currentLayerIndex = 0;
// undo/redo per frame+layer: stack of actions
let undoStack = [];
let redoStack = [];
// playback
let fps = 12;
let playing = false;
let playInterval = null;
// UI refs
const colorPicker = document.getElementById('colorPicker');
const brushSizeInput = document.getElementById('brushSize');
const fpsInput = document.getElementById('fps');
const framesListEl = document.getElementById('framesList');
const addFrameBtn = document.getElementById('addFrame');
const dupFrameBtn = document.getElementById('addFrame'); // reused
const delFrameBtn = document.getElementById('delFrame'); // not in DOM (we rely on frame context menu)
const layersListEl = document.getElementById('layersList');
const addLayerBtn = document.getElementById('addLayer');
const delLayerBtn = document.getElementById('delLayer');
const undoBtn = document.getElementById('undo');
const redoBtn = document.getElementById('redo');
const playBtn = document.getElementById('playPause');
const zoomInBtn = document.getElementById('zoomIn');
const zoomOutBtn = document.getElementById('zoomOut');
const zoomVal = document.getElementById('zoomVal');
const canvasWidthInput = document.getElementById('canvasWidth');
const canvasHeightInput = document.getElementById('canvasHeight');
const applySizeBtn = document.getElementById('applySize');
const audioFileInput = document.getElementById('audioFile');
const audioStartInput =...