AquaMotion

by Sharxxy

HTML

<h1>AquaMotion 🎨</h1>
<div id="toolbar">
  <button id="drawBtn">✏️ Draw</button>
  <button id="eraseBtn">🩹 Erase</button>
  <input type="color" id="colorPicker" value="#0077ff">
  <input type="range" id="brushSize" min="2" max="30" value="6">
  <button id="newFrame">➕ Frame</button>
  <button id="deleteFrame">🗑️ Delete Frame</button>
  <button id="play">▶️ Play</button>
  <label>FPS: <input type="number" id="fps" value="6" min="1" max="24"></label>
</div>

<canvas id="drawCanvas" width="800" height="500"></canvas>
<div id="frames"></div>

CSS

body {
  font-family: Arial, sans-serif;
  background: #e0f7fa;
  color: #0077ff;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 1rem;
}

h1 {
  margin-bottom: 1rem;
}

#toolbar {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
  flex-wrap: wrap;
}

#toolbar button {
  padding: 0.4rem 0.7rem;
}

canvas {
  border: 2px solid #0077ff;
  background: #ffffff;
  cursor: crosshair;
}

#frames {
  margin-top: 1rem;
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.frame {
  width: 60px;
  height: 40px;
  border: 1px solid #aaa;
  background: #ffffff;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 0.7rem;
  cursor: pointer;
}

.frame.active {
  border: 2px solid #0077ff;
}

JavaScript

const canvas = document.getElementById("drawCanvas");
const ctx = canvas.getContext("2d");

let tool = "draw";
let color = document.getElementById("colorPicker").value;
let brushSize = 6;
let drawing = false;

let frames = [[]];
let currentFrame = 0;
let fps = 6;
let playing = false;
let currentPath = null;

// DRAWING FUNCTIONS
function startDraw(e) {
  drawing = true;
  currentPath = {
    color: tool === "erase" ? "erase" : color,
    size: brushSize,
    points: [{ x: e.offsetX, y: e.offsetY }]
  };
}

function drawMove(e) {
  if (!drawing) return;
  currentPath.points.push({ x: e.offsetX, y: e.offsetY });
  redraw();
}

function endDraw() {
  if (drawing && currentPath) frames[currentFrame].push(currentPath);
  currentPath = null;
  drawing = false;
}

function drawPath(path) {
  ctx.lineCap = "round";
  ctx.lineJoin = "round";
  ctx.lineWidth = path.size;
  ctx.beginPath();
  ctx.moveTo(path.points[0].x, path.points[0].y);
  path.points.forEach(p => ctx.lineTo(p.x, p.y));
  if (path.color === "erase") {
    ctx.globalCompositeOperation = "destination-out";
    ctx.strokeStyle = "rgba(0,0,0,1)";
  } else {
    ctx.globalCompositeOperation = "source-over";
    ctx.strokeStyle = path.color;
  }
  ctx.stroke();
}

function redraw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  frames[currentFrame].forEach(drawPath);
  if (currentPath) drawPath(currentPath);

  // Onion skin previous frame
  if (frames[currentFrame - 1]) {
    ctx.save();
    ctx.globalAlpha = 0.3;
    frames[currentFrame - 1].forEach(drawPath);
    ctx.restore();
  }
}

// TOOLBAR EVENTS
document.getElementById("drawBtn").onclick = () => tool = "draw";
document.getElementById("eraseBtn").onclick = () => tool = "erase";
document.getElementById("colorPicker").oninput = (e) => color = e.target.value;
document.getElementById("brushSize").oninput = (e) => brushSize =...