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 =...