PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / drawing-canvas / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/drawing-canvas/frontend.js

371 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 function typoCssVarsForEl(typo, prefix, el) {
5 if (!typo || typeof typo !== 'object') return;
6 var m = { family:'font-family', weight:'font-weight', transform:'text-transform', style:'font-style', decoration:'text-decoration',
7 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
8 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
9 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
10 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' };
11 Object.keys(m).forEach(function (k) {
12 if (typo[k] !== undefined && typo[k] !== '') {
13 var v = typo[k], u = typo[k + 'Unit'] || '';
14 if (/Desktop|Tablet|Mobile/.test(k) && typeof v === 'number') v = v + (u || 'px');
15 el.style.setProperty(prefix + m[k], '' + v);
16 }
17 });
18 }
19
20 var PRESET_PALETTE = [
21 '#000000', '#374151', '#6b7280', '#d1d5db',
22 '#ffffff', '#ef4444', '#f97316', '#eab308',
23 '#22c55e', '#06b6d4', '#6366f1', '#a855f7'
24 ];
25
26 var MAX_HISTORY = 40;
27
28 function initBlock(root) {
29 var optsRaw = root.getAttribute('data-opts');
30 var opts;
31 try { opts = JSON.parse(optsRaw); } catch (e) { opts = {}; }
32
33 var accent = opts.accentColor || '#6366f1';
34 var canvW = opts.canvasWidth || 800;
35 var canvH = opts.canvasHeight || 480;
36 var defTool = opts.defaultTool || 'pen';
37 var defColor = opts.defaultColor || '#1e1b4b';
38 var defLineWidth = opts.defaultLineWidth || 4;
39 var showGrid = !!opts.showGrid;
40 var bgColor = opts.backgroundColor || '#ffffff';
41 var gridCol = opts.gridColor || '#e5e7eb';
42 var titleColor = opts.titleColor || '#1e1b4b';
43 var sectionBg = opts.sectionBg || '#f8fafc';
44
45 if (sectionBg) root.style.background = sectionBg;
46 root.style.setProperty('--bkbg-drw-accent', accent);
47 root.style.setProperty('--bkbg-drw-ttl-fs', (opts.fontSize || 26) + 'px');
48 root.style.setProperty('--bkbg-drw-sub-fs', (opts.subtitleSize || 14) + 'px');
49 typoCssVarsForEl(opts.typoTitle, '--bkbg-drw-ttl-', root);
50 typoCssVarsForEl(opts.typoSubtitle, '--bkbg-drw-sub-', root);
51
52 var titleEl = root.querySelector('.bkbg-drw-title');
53 if (titleEl) { titleEl.style.color = titleColor; }
54 var subEl = root.querySelector('.bkbg-drw-subtitle');
55 if (subEl) { subEl.style.color = titleColor; }
56
57 // ---- State ----
58 var tool = defTool;
59 var color = defColor;
60 var lineWidth = defLineWidth;
61 var isDrawing = false;
62 var startX = 0, startY = 0;
63 var history = [];
64 var snapshot = null; // imageData snapshot for shape preview during drag
65
66 // ---- Build DOM ----
67 var app = document.createElement('div');
68
69 // Toolbar
70 var toolbarEl;
71 if (opts.showToolbar !== false) {
72 toolbarEl = document.createElement('div');
73 toolbarEl.className = 'bkbg-drw-toolbar';
74 app.appendChild(toolbarEl);
75 }
76
77 // Canvas wrapper + canvas
78 var wrap = document.createElement('div');
79 wrap.className = 'bkbg-drw-canvas-wrap';
80
81 var canvas = document.createElement('canvas');
82 canvas.className = 'bkbg-drw-canvas';
83 canvas.width = canvW;
84 canvas.height = canvH;
85 canvas.style.background = bgColor;
86 wrap.appendChild(canvas);
87 app.appendChild(wrap);
88
89 // Action bar
90 var actionsEl = document.createElement('div');
91 actionsEl.className = 'bkbg-drw-actions';
92 app.appendChild(actionsEl);
93
94 root.appendChild(app);
95
96 var ctx = canvas.getContext('2d');
97 ctx.lineCap = 'round';
98 ctx.lineJoin = 'round';
99
100 // ---- Grid ----
101 function drawGrid() {
102 if (!showGrid) return;
103 var step = 20;
104 ctx.save();
105 ctx.strokeStyle = gridCol;
106 ctx.lineWidth = 0.5;
107 ctx.globalAlpha = 0.5;
108 for (var x = step; x < canvW; x += step) {
109 ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvH); ctx.stroke();
110 }
111 for (var y = step; y < canvH; y += step) {
112 ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvW, y); ctx.stroke();
113 }
114 ctx.restore();
115 }
116
117 // ---- Init canvas ----
118 function initCanvas() {
119 ctx.fillStyle = bgColor;
120 ctx.fillRect(0, 0, canvW, canvH);
121 drawGrid();
122 }
123 initCanvas();
124
125 // ---- History ----
126 function saveHistory() {
127 if (history.length >= MAX_HISTORY) history.shift();
128 history.push(ctx.getImageData(0, 0, canvW, canvH));
129 }
130
131 function undo() {
132 if (!history.length) return;
133 ctx.putImageData(history.pop(), 0, 0);
134 }
135
136 // ---- Coordinate helper ---- (accounts for canvas CSS scaling)
137 function getPos(e) {
138 var rect = canvas.getBoundingClientRect();
139 var scaleX = canvW / rect.width;
140 var scaleY = canvH / rect.height;
141 var src = e.touches ? e.touches[0] : e;
142 return {
143 x: (src.clientX - rect.left) * scaleX,
144 y: (src.clientY - rect.top) * scaleY
145 };
146 }
147
148 // ---- Drawing helpers ----
149 function applyTool(x, y) {
150 ctx.strokeStyle = tool === 'eraser' ? bgColor : color;
151 ctx.lineWidth = lineWidth;
152 ctx.globalAlpha = tool === 'marker' ? 0.45 : 1;
153 ctx.lineTo(x, y);
154 ctx.stroke();
155 }
156
157 function drawShape(x, y) {
158 if (!snapshot) return;
159 ctx.putImageData(snapshot, 0, 0);
160 ctx.strokeStyle = color;
161 ctx.lineWidth = lineWidth;
162 ctx.globalAlpha = 1;
163 if (tool === 'line') {
164 ctx.beginPath();
165 ctx.moveTo(startX, startY);
166 ctx.lineTo(x, y);
167 ctx.stroke();
168 } else if (tool === 'rect') {
169 ctx.beginPath();
170 ctx.strokeRect(startX, startY, x - startX, y - startY);
171 } else if (tool === 'ellipse') {
172 var rx = Math.abs(x - startX) / 2;
173 var ry = Math.abs(y - startY) / 2;
174 var cx = startX + (x - startX) / 2;
175 var cy = startY + (y - startY) / 2;
176 ctx.beginPath();
177 ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
178 ctx.stroke();
179 }
180 }
181
182 // ---- Flood fill (for fill tool if added) ----
183 // Not included in toolbar by default; shapes + pen/marker cover most use-cases.
184
185 // ---- Mouse events ----
186 function onStart(x, y) {
187 saveHistory();
188 isDrawing = true;
189 startX = x; startY = y;
190 if (tool === 'pen' || tool === 'marker' || tool === 'eraser') {
191 ctx.save();
192 ctx.strokeStyle = tool === 'eraser' ? bgColor : color;
193 ctx.lineWidth = lineWidth;
194 ctx.globalAlpha = tool === 'marker' ? 0.45 : 1;
195 ctx.lineCap = 'round';
196 ctx.lineJoin = 'round';
197 ctx.beginPath();
198 ctx.moveTo(x, y);
199 } else {
200 // shape — take snapshot
201 snapshot = ctx.getImageData(0, 0, canvW, canvH);
202 }
203 }
204
205 function onMove(x, y) {
206 if (!isDrawing) return;
207 if (tool === 'pen' || tool === 'marker' || tool === 'eraser') {
208 applyTool(x, y);
209 } else {
210 drawShape(x, y);
211 }
212 }
213
214 function onEnd(x, y) {
215 if (!isDrawing) return;
216 isDrawing = false;
217 if (tool === 'pen' || tool === 'marker' || tool === 'eraser') {
218 ctx.restore();
219 } else {
220 drawShape(x, y);
221 snapshot = null;
222 }
223 }
224
225 canvas.addEventListener('mousedown', function (e) { var p = getPos(e); onStart(p.x, p.y); });
226 canvas.addEventListener('mousemove', function (e) { var p = getPos(e); onMove(p.x, p.y); });
227 canvas.addEventListener('mouseup', function (e) { var p = getPos(e); onEnd(p.x, p.y); });
228 canvas.addEventListener('mouseleave', function (e) { if (isDrawing) { var p = getPos(e); onEnd(p.x, p.y); } });
229
230 canvas.addEventListener('touchstart', function (e) { e.preventDefault(); var p = getPos(e); onStart(p.x, p.y); }, { passive: false });
231 canvas.addEventListener('touchmove', function (e) { e.preventDefault(); var p = getPos(e); onMove(p.x, p.y); }, { passive: false });
232 canvas.addEventListener('touchend', function (e) { e.preventDefault(); if (e.changedTouches.length) { var t = e.changedTouches[0]; var rect = canvas.getBoundingClientRect(); onEnd((t.clientX - rect.left) * canvW / rect.width, (t.clientY - rect.top) * canvH / rect.height); } }, { passive: false });
233
234 // Update cursor
235 function updateCursor() {
236 canvas.className = 'bkbg-drw-canvas' + (tool === 'eraser' ? ' bkbg-drw-eraser-cur' : '');
237 }
238
239 // ---- Build Toolbar ----
240 if (toolbarEl) {
241 var TOOLS = [
242 { id: 'pen', label: '✏️', title: 'Pen (freehand)' },
243 { id: 'marker', label: '🖊️', title: 'Marker (soft)' },
244 { id: 'line', label: '📏', title: 'Line' },
245 { id: 'rect', label: '', title: 'Rectangle' },
246 { id: 'ellipse', label: '', title: 'Ellipse' },
247 { id: 'eraser', label: '🧹', title: 'Eraser' }
248 ];
249
250 var toolBtns = {};
251 TOOLS.forEach(function (t) {
252 var btn = document.createElement('button');
253 btn.className = 'bkbg-drw-tool-btn' + (t.id === tool ? ' bkbg-drw-active' : '');
254 btn.title = t.title;
255 btn.textContent = t.label;
256 btn.addEventListener('click', function () {
257 tool = t.id;
258 Object.values(toolBtns).forEach(function (b) { b.classList.remove('bkbg-drw-active'); });
259 btn.classList.add('bkbg-drw-active');
260 updateCursor();
261 });
262 toolbarEl.appendChild(btn);
263 toolBtns[t.id] = btn;
264 });
265
266 // Separator
267 var sep = document.createElement('div');
268 sep.className = 'bkbg-drw-toolbar-sep';
269 toolbarEl.appendChild(sep);
270
271 // Palette
272 var palette = document.createElement('div');
273 palette.className = 'bkbg-drw-palette';
274
275 var swatches = [];
276 PRESET_PALETTE.forEach(function (hex) {
277 var sw = document.createElement('div');
278 sw.className = 'bkbg-drw-swatch' + (hex === color ? ' bkbg-drw-selected' : '');
279 sw.style.background = hex;
280 sw.title = hex;
281 sw.addEventListener('click', function () {
282 color = hex;
283 swatches.forEach(function (s) { s.classList.remove('bkbg-drw-selected'); });
284 sw.classList.add('bkbg-drw-selected');
285 customColor.value = hex;
286 });
287 palette.appendChild(sw);
288 swatches.push(sw);
289 });
290
291 // Custom color picker
292 var customColor = document.createElement('input');
293 customColor.type = 'color';
294 customColor.className = 'bkbg-drw-custom-color';
295 customColor.title = 'Custom color';
296 customColor.value = color;
297 customColor.addEventListener('input', function () {
298 color = customColor.value;
299 swatches.forEach(function (s) { s.classList.remove('bkbg-drw-selected'); });
300 });
301 palette.appendChild(customColor);
302 toolbarEl.appendChild(palette);
303
304 // Separator
305 var sep2 = document.createElement('div');
306 sep2.className = 'bkbg-drw-toolbar-sep';
307 toolbarEl.appendChild(sep2);
308
309 // Stroke width
310 var strokeWrap = document.createElement('div');
311 strokeWrap.className = 'bkbg-drw-stroke-wrap';
312 var strokeLbl = document.createElement('span');
313 strokeLbl.className = 'bkbg-drw-stroke-label';
314 strokeLbl.textContent = 'Size';
315 var strokeRange = document.createElement('input');
316 strokeRange.type = 'range';
317 strokeRange.className = 'bkbg-drw-stroke-range';
318 strokeRange.min = 1;
319 strokeRange.max = 60;
320 strokeRange.value = lineWidth;
321 strokeRange.addEventListener('input', function () { lineWidth = parseInt(strokeRange.value, 10); });
322 strokeWrap.appendChild(strokeLbl);
323 strokeWrap.appendChild(strokeRange);
324 toolbarEl.appendChild(strokeWrap);
325 }
326
327 // ---- Action buttons ----
328 if (opts.showUndo !== false) {
329 var undoBtn = document.createElement('button');
330 undoBtn.className = 'bkbg-drw-btn';
331 undoBtn.textContent = '↩ Undo';
332 undoBtn.addEventListener('click', undo);
333 actionsEl.appendChild(undoBtn);
334 }
335
336 var clearBtn = document.createElement('button');
337 clearBtn.className = 'bkbg-drw-btn bkbg-drw-clear-btn';
338 clearBtn.textContent = '🗑 Clear';
339 clearBtn.addEventListener('click', function () {
340 if (!confirm('Clear the canvas? This cannot be undone.')) return;
341 saveHistory();
342 initCanvas();
343 });
344 actionsEl.appendChild(clearBtn);
345
346 if (opts.showDownload !== false) {
347 var dlBtn = document.createElement('button');
348 dlBtn.className = 'bkbg-drw-btn bkbg-drw-dl-btn';
349 dlBtn.style.background = accent;
350 dlBtn.textContent = '⬇ Download PNG';
351 dlBtn.addEventListener('click', function () {
352 canvas.toBlob(function (blob) {
353 var url = URL.createObjectURL(blob);
354 var a = document.createElement('a');
355 a.href = url;
356 a.download = 'drawing.png';
357 a.click();
358 setTimeout(function () { URL.revokeObjectURL(url); }, 2000);
359 }, 'image/png');
360 });
361 actionsEl.appendChild(dlBtn);
362 }
363
364 updateCursor();
365 }
366
367 document.querySelectorAll('.bkbg-drw-app').forEach(function (root) {
368 initBlock(root);
369 });
370 })();
371