Bug fixes!
This commit is contained in:
parent
8abf9bdcdf
commit
4fe3dfb8b4
1 changed files with 133 additions and 37 deletions
|
|
@ -71,6 +71,24 @@
|
|||
</svg>
|
||||
</label>
|
||||
|
||||
<input type="radio" class="btn-check" id="outlineEllipse" name="tool">
|
||||
<label for="outlineEllipse"
|
||||
class="btn btn-sm btn-light border d-inline-flex align-items-center justify-content-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-circle" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16" />
|
||||
</svg>
|
||||
</label>
|
||||
|
||||
<input type="radio" class="btn-check" id="filledEllipse" name="tool">
|
||||
<label for="filledEllipse"
|
||||
class="btn btn-sm btn-light border d-inline-flex align-items-center justify-content-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-circle-fill" viewBox="0 0 16 16">
|
||||
<circle cx="8" cy="8" r="8" />
|
||||
</svg>
|
||||
</label>
|
||||
|
||||
<input type="radio" class="btn-check" id="line" name="tool">
|
||||
<label for="line"
|
||||
class="btn btn-sm btn-light border d-inline-flex align-items-center justify-content-center">
|
||||
|
|
@ -198,6 +216,13 @@ window.addEventListener('resize', resizeAndSetupCanvas);
|
|||
|
||||
setGrid();
|
||||
|
||||
function axisMode(type, axis) {
|
||||
if (type === 'fullGrid') return 'grid';
|
||||
if (type === 'verticalGrid') return axis === 'x' ? 'grid' : 'px';
|
||||
if (type === 'horizontalGrid') return axis === 'y' ? 'grid' : 'px';
|
||||
return 'px'; // noGrid
|
||||
}
|
||||
|
||||
function getActiveTool() {
|
||||
const checked = document.querySelector('input[name="tool"]:checked');
|
||||
return checked ? checked.id : 'outline';
|
||||
|
|
@ -260,40 +285,67 @@ function snapToGrid(x, y) {
|
|||
};
|
||||
}
|
||||
|
||||
function normalizeRect(shape) {
|
||||
const ix1 = Math.round(shape.x1 / {{ grid_size }});
|
||||
const iy1 = Math.round(shape.y1 / {{ grid_size }});
|
||||
const ix2 = Math.round(shape.x2 / {{ grid_size }});
|
||||
const iy2 = Math.round(shape.y2 / {{ grid_size }});
|
||||
function normRange(a1, a2, mode, grid) {
|
||||
if (mode === 'grid') {
|
||||
const i1 = Math.round(a1 / grid);
|
||||
const i2 = Math.round(a2 / grid);
|
||||
const start = Math.min(i1, i2);
|
||||
const size = Math.abs(i2 - i1);
|
||||
return { start, size };
|
||||
} else {
|
||||
const start = Math.min(a1, a2);
|
||||
const size = Math.abs(a2 - a1);
|
||||
return { start, size };
|
||||
}
|
||||
}
|
||||
|
||||
const ix = Math.min(ix1, ix2);
|
||||
const iy = Math.min(iy1, iy2);
|
||||
const iw = Math.abs(ix2 - ix1);
|
||||
const ih = Math.abs(iy2 - iy1);
|
||||
function normalizeRect(shape) {
|
||||
const grid = {{ grid_size }};
|
||||
const type = getActiveType();
|
||||
|
||||
const modeX = axisMode(type, 'x');
|
||||
const modeY = axisMode(type, 'y');
|
||||
|
||||
const xr = normRange(shape.x1, shape.x2, modeX, grid);
|
||||
const yr = normRange(shape.y1, shape.y2, modeY, grid);
|
||||
|
||||
const x = (modeX === 'grid') ? xr.start : xr.start;
|
||||
const w = (modeX === 'grid') ? xr.size : xr.size;
|
||||
const y = (modeX === 'grid') ? yr.start : yr.start;
|
||||
const h = (modeX === 'grid') ? yr.size : yr.size;
|
||||
|
||||
return {
|
||||
type: 'rect',
|
||||
ix,
|
||||
iy,
|
||||
iw,
|
||||
ih,
|
||||
modeX, modeY,
|
||||
x, y, w, h,
|
||||
color: shape.color,
|
||||
fill: shape.fill
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeEllipse(shape) {
|
||||
const r = normalizeRect(shape);
|
||||
return { ...r, type: 'ellipse' };
|
||||
}
|
||||
|
||||
function normalizeLine(shape) {
|
||||
const ix1 = shape.x1 / {{ grid_size }};
|
||||
const iy1 = shape.y1 / {{ grid_size }};
|
||||
const ix2 = shape.x2 / {{ grid_size }};
|
||||
const iy2 = shape.y2 / {{ grid_size }};
|
||||
const grid = {{ grid_size }};
|
||||
const type = getActiveType();
|
||||
|
||||
const modeX = axisMode(type, 'x');
|
||||
const modeY = axisMode(type, 'y');
|
||||
|
||||
function normPoint(v, mode) {
|
||||
return mode === 'grid' ? (v / grid) : v;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'line',
|
||||
ix1,
|
||||
iy1,
|
||||
ix2,
|
||||
iy2,
|
||||
modeX, modeY,
|
||||
x1: normPoint(shape.x1, modeX),
|
||||
y1: normPoint(shape.y1, modeY),
|
||||
x2: normPoint(shape.x2, modeX),
|
||||
y2: normPoint(shape.y2, modeY),
|
||||
color: shape.color
|
||||
};
|
||||
}
|
||||
|
|
@ -327,28 +379,52 @@ function redrawAll() {
|
|||
function drawShape(shape) {
|
||||
if (!ctx) return;
|
||||
|
||||
const grid = {{ grid_size }};
|
||||
const modeX = shape.modeX || 'grid';
|
||||
const modeY = shape.modeY || 'grid';
|
||||
|
||||
const toPxX = (v) => modeX === 'grid' ? v * grid : v;
|
||||
const toPxY = (v) => modeY === 'grid' ? v * grid : v;
|
||||
|
||||
ctx.save();
|
||||
ctx.strokeStyle = shape.color || '#000000';
|
||||
|
||||
if (shape.type === 'rect') {
|
||||
const x = shape.ix * {{ grid_size }};
|
||||
const y = shape.iy * {{ grid_size }};
|
||||
const w = shape.iw * {{ grid_size }};
|
||||
const h = shape.ih * {{ grid_size }};
|
||||
if (shape.type === 'rect' || shape.type === 'ellipse') {
|
||||
const x = toPxX(shape.x);
|
||||
const y = toPxY(shape.y);
|
||||
const w = toPxX(shape.w);
|
||||
const h = toPxY(shape.h);
|
||||
|
||||
ctx.strokeRect(x, y, w, h);
|
||||
if (shape.type === 'rect') {
|
||||
ctx.strokeRect(x, y, w, h);
|
||||
} else {
|
||||
const cx = x + w / 2;
|
||||
const cy = y + h / 2;
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(cx, cy, Math.abs(w / 2), Math.abs(h / 2), 0, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
if (shape.fill) {
|
||||
ctx.globalAlpha = 0.15;
|
||||
ctx.fillStyle = shape.color;
|
||||
ctx.fillRect(x, y, w, h);
|
||||
if (shape.type === 'rect') {
|
||||
ctx.fillRect(x, y, w, h);
|
||||
} else {
|
||||
const cx = x + w / 2;
|
||||
const cy = y + h / 2;
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(cx, cy, Math.abs(w / 2), Math.abs(h / 2), 0, 0, Math.PI * 2);
|
||||
ctx.fill()
|
||||
}
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
} else if (shape.type === 'line') {
|
||||
const x1 = shape.ix1 * {{ grid_size }};
|
||||
const y1 = shape.iy1 * {{ grid_size }};
|
||||
const x2 = shape.ix2 * {{ grid_size }};
|
||||
const y2 = shape.iy2 * {{ grid_size }};
|
||||
const x1 = toPxX(shape.x1);
|
||||
const y1 = toPxY(shape.y1);
|
||||
const x2 = toPxX(shape.x2);
|
||||
const y2 = toPxY(shape.y2);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
|
|
@ -546,13 +622,20 @@ gridEl.addEventListener('pointermove', (e) => {
|
|||
color: currentShape.color
|
||||
});
|
||||
drawShape(previewLine);
|
||||
} else {
|
||||
} else if (tool === 'filled' || tool === 'outline') {
|
||||
const previewRect = normalizeRect({
|
||||
...currentShape,
|
||||
x2: snapX,
|
||||
y2: snapY
|
||||
});
|
||||
drawShape(previewRect);
|
||||
} else if (tool === 'filledEllipse' || tool === 'outlineEllipse') {
|
||||
const previewEllipse = normalizeEllipse({
|
||||
...currentShape,
|
||||
x2: snapX,
|
||||
y2: snapY
|
||||
});
|
||||
drawShape(previewEllipse);
|
||||
}
|
||||
|
||||
ctx.setLineDash([]);
|
||||
|
|
@ -586,7 +669,7 @@ gridEl.addEventListener('pointerdown', (e) => {
|
|||
y2: snapY,
|
||||
color: selectedColor
|
||||
};
|
||||
} else {
|
||||
} else if (tool === 'outline' || tool === 'filled') {
|
||||
currentShape = {
|
||||
tool,
|
||||
x1: snapX,
|
||||
|
|
@ -596,6 +679,16 @@ gridEl.addEventListener('pointerdown', (e) => {
|
|||
color: selectedColor,
|
||||
fill: document.getElementById('filled').checked
|
||||
};
|
||||
} else if (tool === 'outlineEllipse' || tool === 'filledEllipse') {
|
||||
currentShape = {
|
||||
tool,
|
||||
x1: snapX,
|
||||
y1: snapY,
|
||||
x2: snapX,
|
||||
y2: snapY,
|
||||
color: selectedColor,
|
||||
fill: (tool === 'filledEllipse')
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -616,15 +709,18 @@ window.addEventListener('pointerup', (e) => {
|
|||
if (currentShape.tool === 'line') {
|
||||
const line = normalizeLine(currentShape);
|
||||
|
||||
if (line.ix1 !== line.ix2 || line.iy1 !== line.iy2) {
|
||||
if (line.x1 !== line.x2 || line.y1 !== line.y2) {
|
||||
finalShape = line;
|
||||
}
|
||||
} else {
|
||||
} else if (currentShape.tool === 'filled' || currentShape.tool === 'outline') {
|
||||
const rect = normalizeRect(currentShape);
|
||||
|
||||
if (rect.iw > 0 && rect.ih > 0) {
|
||||
if (rect.w > 0 && rect.h > 0) {
|
||||
finalShape = rect;
|
||||
}
|
||||
} else if (currentShape.tool === 'filledEllipse' || currentShape.tool === 'outlineEllipse') {
|
||||
const ellipse = normalizeEllipse(currentShape);
|
||||
if (ellipse.w > 0 && ellipse.h > 0) finalShape = ellipse;
|
||||
}
|
||||
|
||||
if (finalShape) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue