AquaMotion π
by Sharxxy
HTML
<header>π AquaMotion - Animation App Prototype</header>
<div id="toolbar">
<button data-tool="brush">ποΈ</button>
<button data-tool="eraser">π§½</button>
<button data-tool="line">π</button>
<button data-tool="rect">β¬</button>
<button data-tool="circle">βͺ</button>
<button data-tool="bucket">πͺ£</button>
<button data-tool="picker">π¨</button>
Brush Size: <input type="range" id="brushSize" min="1" max="30" value="5">
Color: <input type="color" id="colorPicker" value="#000000">
<button id="addFrame">β Frame</button>
<button id="play">βΆ Play</button>
<button id="undo">β© Undo</button>
<button id="redo">βͺ Redo</button>
<button id="copy">π Copy</button>
<button id="paste">π₯ Paste</button>
<button id="save">πΎ Save .aqua</button>
<button id="load">π Load .aqua</button>
<button id="export">π€ Export PNGs</button>
<input type="file" id="fileInput" style="display:none">
<button id="addLayer">β Layer</button>
<button id="addAudio">π΅ Add Audio</button>
<input type="file" id="audioInput" accept="audio/*" style="display:none">
<label>Canvas W: <input type="number" id="canvasW" value="600" min="100"></label>
<label>H: <input type="number" id="canvasH" value="400" min="100"></label>
<button id="resizeCanvas">π Resize</button>
<label><input type="checkbox" id="onionSkin"> Onion Skin</label>
</div>
<div id="main">
<div id="canvas-container">
<canvas id="drawCanvas" width="600" height="400"></canvas>
</div>
<div style="flex:1;display:flex;flex-direction:column;">
<div id="timeline"></div>
<div id="layers"></div>
</div>
</div>
<audio id="audioPlayer" controls style="width:100%;"></audio>
CSS
body {
margin: 0;
font-family: Arial, sans-serif;
background: linear-gradient(180deg, #0a2540, #0f5c8d);
color: white;
display: flex;
flex-direction: column;
height: 100vh;
}
header {
padding: 10px;
background: rgba(0, 128, 192, 0.9);
text-align: center;
font-size: 22px;
font-weight: bold;
color: #fff;
letter-spacing: 2px;
text-shadow: 0 0 5px #00d4ff;
}
#toolbar {
background: rgba(0, 50, 80, 0.9);
padding: 6px;
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
#toolbar button, #toolbar input {
cursor: pointer;
padding: 4px 8px;
border: none;
border-radius: 4px;
}
#toolbar button {
background: #00aaff;
color: white;
}
#toolbar button:hover {
background: #008ecc;
}
#main {
flex: 1;
display: flex;
overflow: hidden;
}
#canvas-container {
flex: 3;
background: #222;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
canvas {
background: white;
border: 2px solid #00bfff;
}
#timeline {
flex: 1;
background: rgba(0, 40, 70, 0.9);
padding: 10px;
overflow-x: auto;
white-space: nowrap;
}
.frame {
display: inline-block;
width: 40px;
height: 40px;
margin: 2px;
background: #444;
border: 2px solid transparent;
cursor: pointer;
}
.frame.active {
border: 2px solid #00adee;
}
#layers {
background: rgba(0, 60, 100, 0.9);
padding: 6px;
color: white;
height: 150px;
overflow-y: auto;
}
.layer {
background: #006699;
margin: 4px 0;
padding: 4px;
cursor: pointer;
}
.layer.active {
...
JavaScript
const canvas = document.getElementById('drawCanvas');
const ctx = canvas.getContext('2d');
let drawing = false;
let tool = "brush";
let brushSize = document.getElementById('brushSize').value;
let brushColor = document.getElementById('colorPicker').value;
let undoStack = [];
let redoStack = [];
let frames = [[]];
let currentFrame = 0;
let layers = [[]];
let currentLayer = 0;
let copiedImage = null;
// Toolbar
document.querySelectorAll('#toolbar button[data-tool]').forEach(btn=>{
btn.onclick=()=>{tool=btn.dataset.tool;}
});
document.getElementById('brushSize').oninput=e=>brushSize=e.target.value;
document.getElementById('colorPicker').oninput=e=>brushColor=e.target.value;
// Layers
function renderLayers() {
const container=document.getElementById('layers');
container.innerHTML='';
layers.forEach((l,i)=>{
const div=document.createElement('div');
div.className='layer'+(i===currentLayer?' active':'');
div.textContent='Layer '+(i+1);
div.onclick=()=>{currentLayer=i;renderLayers();drawFrame();}
container.appendChild(div);
});
}
document.getElementById('addLayer').onclick=()=>{
layers[currentFrame].push(ctx.createImageData(canvas.width,canvas.height));
renderLayers();
};
// Timeline
function renderTimeline() {
const timeline=document.getElementById('timeline');
timeline.innerHTML='';
frames.forEach((f,i)=>{
const div=document.createElement('div');
div.className='frame'+(i===currentFrame?' active':'');
div.onclick=()=>{currentFrame=i;drawFrame();renderTimeline();}
timeline.appendChild(div);
});
}
document.getElementById('addFrame').onclick=()=>{
frames.push([]);
layers.push([ctx.createImageData(canvas.width,canvas.height)]);
currentFrame=frames.length-1;
...