Spaces:
Running
Running
File size: 5,889 Bytes
b747299 c317bf2 b747299 c317bf2 91bc898 b747299 c317bf2 91bc898 c317bf2 b747299 c317bf2 91bc898 26fbc3b c317bf2 26fbc3b c317bf2 91bc898 c317bf2 b747299 c317bf2 91bc898 c317bf2 91bc898 c317bf2 b747299 3d5d9f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Tailwind CSS (Play CDN) -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- FontAwesome -->
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
/>
<!-- Fabric.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/6.6.4/fabric.min.js"></script>
<!-- fabric-brushes addon -->
<script src="https://cdn.jsdelivr.net/gh/av01d/fabric-brushes@1.0/dist/fabric.brushes.min.js"></script>
</head>
<body class="h-screen flex bg-gray-100">
<!-- Layers Panel -->
<aside class="w-64 bg-white border-r p-4 flex flex-col">
<h2 class="text-xl font-semibold mb-4">Layers</h2>
<div id="layers" class="flex-1 overflow-auto space-y-2"></div>
<button id="add-layer"
class="mt-4 flex items-center justify-center px-3 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
<i class="fa fa-plus mr-2"></i>Add Layer
</button>
</aside>
<!-- Main Canvas Area -->
<main class="flex-1 flex flex-col">
<!-- Brush Toolbar -->
<div class="p-4 bg-white border-b flex space-x-4">
<button class="brush-btn" data-brush="pencil" title="Pencil">
<i class="fa fa-pencil-alt fa-lg"></i>
</button>
<button class="brush-btn" data-brush="chalk" title="Chalk">
<i class="fa fa-chalkboard fa-lg"></i>
</button>
<button class="brush-btn" data-brush="paint" title="Paint">
<i class="fa fa-paint-brush fa-lg"></i>
</button>
<button class="brush-btn" data-brush="splatter" title="Splatter">
<i class="fa fa-spray-can fa-lg"></i>
</button>
</div>
<!-- Canvas Stack -->
<div id="canvas-wrapper" class="relative flex-1 bg-white"></div>
</main>
<!-- Script -->
<script>
const layersContainer = document.getElementById('layers');
const addLayerBtn = document.getElementById('add-layer');
const canvasWrapper = document.getElementById('canvas-wrapper');
let layers = [], current = -1;
// Create a new layer
function addLayer(name='Layer') {
const id = Date.now().toString();
// Create <canvas>
const c = document.createElement('canvas');
c.id = 'canvas-' + id;
c.width = canvasWrapper.clientWidth;
c.height = canvasWrapper.clientHeight;
c.classList.add('absolute','top-0','left-0');
canvasWrapper.appendChild(c);
// Init Fabric canvas
const fc = new fabric.Canvas(c.id, {
isDrawingMode: true,
backgroundColor: 'transparent'
});
fc.freeDrawingBrush = new fabric.PencilBrush(fc); // default :contentReference[oaicite:5]{index=5}
layers.push({ id, name, el: c, fc });
selectLayer(layers.length - 1);
updateLayersUI();
}
// Refresh the layers panel
function updateLayersUI() {
layersContainer.innerHTML = '';
layers.forEach((ly, i) => {
const div = document.createElement('div');
div.className = [
'flex items-center justify-between p-2 rounded',
i===current ? 'bg-blue-100' : 'hover:bg-gray-100 cursor-pointer'
].join(' ');
div.innerHTML = `
<span>${ly.name}</span>
<div class="space-x-1">
<button onclick="moveLayer(${i},-1)"><i class="fa fa-arrow-up"></i></button>
<button onclick="moveLayer(${i},+1)"><i class="fa fa-arrow-down"></i></button>
<button onclick="removeLayer(${i})"><i class="fa fa-trash"></i></button>
</div>`;
div.addEventListener('click',()=>selectLayer(i));
layersContainer.appendChild(div);
});
// Stack canvases by index
layers.forEach((ly,i)=> {
ly.el.style.zIndex = i;
});
if(current>=0) layers[current].el.style.zIndex = layers.length;
}
// Select a layer
function selectLayer(i) {
current = i;
layers.forEach((ly,idx)=>{
ly.fc.selection = (idx===i);
});
updateLayersUI();
}
// Remove a layer
function removeLayer(i) {
canvasWrapper.removeChild(layers[i].el);
layers.splice(i,1);
if(current >= layers.length) current = layers.length -1;
updateLayersUI();
}
// Move layer up/down
function moveLayer(i, dir) {
const j = i + dir;
if(j<0||j>=layers.length) return;
[layers[i], layers[j]] = [layers[j], layers[i]];
if(current === i) current = j;
else if(current === j) current = i;
updateLayersUI();
}
// Change brush type
document.querySelectorAll('.brush-btn').forEach(btn=>{
btn.addEventListener('click',()=>{
if(current<0) return;
const f = layers[current].fc;
switch(btn.dataset.brush) {
case 'pencil':
f.freeDrawingBrush = new fabric.PencilBrush(f); break;
case 'chalk':
f.freeDrawingBrush = new fabric.SketchyBrush(f,{
width: 10, color:'#222', opacity:0.6
}); break; // Sketchy for chalk :contentReference[oaicite:6]{index=6}
case 'paint':
f.freeDrawingBrush = new fabric.RibbonBrush(f,{
width: 20, color:'#c00', opacity:0.8
}); break; // Ribbon for paint drips :contentReference[oaicite:7]{index=7}
case 'splatter':
f.freeDrawingBrush = new fabric.SpraypaintBrush(f,{
width: 30, color:'#008', opacity:0.7
}); break; // Spraypaint for splatter :contentReference[oaicite:8]{index=8}
}
});
});
// Init with one layer
window.addEventListener('DOMContentLoaded',()=> addLayer('Layer 1'));
addLayerBtn.addEventListener('click',()=>addLayer(`Layer ${layers.length+1}`));
</script>
</body>
</html> |