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>
|
</svg>
|
||||||
</label>
|
</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">
|
<input type="radio" class="btn-check" id="line" name="tool">
|
||||||
<label for="line"
|
<label for="line"
|
||||||
class="btn btn-sm btn-light border d-inline-flex align-items-center justify-content-center">
|
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();
|
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() {
|
function getActiveTool() {
|
||||||
const checked = document.querySelector('input[name="tool"]:checked');
|
const checked = document.querySelector('input[name="tool"]:checked');
|
||||||
return checked ? checked.id : 'outline';
|
return checked ? checked.id : 'outline';
|
||||||
|
|
@ -260,40 +285,67 @@ function snapToGrid(x, y) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeRect(shape) {
|
function normRange(a1, a2, mode, grid) {
|
||||||
const ix1 = Math.round(shape.x1 / {{ grid_size }});
|
if (mode === 'grid') {
|
||||||
const iy1 = Math.round(shape.y1 / {{ grid_size }});
|
const i1 = Math.round(a1 / grid);
|
||||||
const ix2 = Math.round(shape.x2 / {{ grid_size }});
|
const i2 = Math.round(a2 / grid);
|
||||||
const iy2 = Math.round(shape.y2 / {{ grid_size }});
|
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);
|
function normalizeRect(shape) {
|
||||||
const iy = Math.min(iy1, iy2);
|
const grid = {{ grid_size }};
|
||||||
const iw = Math.abs(ix2 - ix1);
|
const type = getActiveType();
|
||||||
const ih = Math.abs(iy2 - iy1);
|
|
||||||
|
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 {
|
return {
|
||||||
type: 'rect',
|
type: 'rect',
|
||||||
ix,
|
modeX, modeY,
|
||||||
iy,
|
x, y, w, h,
|
||||||
iw,
|
|
||||||
ih,
|
|
||||||
color: shape.color,
|
color: shape.color,
|
||||||
fill: shape.fill
|
fill: shape.fill
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeEllipse(shape) {
|
||||||
|
const r = normalizeRect(shape);
|
||||||
|
return { ...r, type: 'ellipse' };
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeLine(shape) {
|
function normalizeLine(shape) {
|
||||||
const ix1 = shape.x1 / {{ grid_size }};
|
const grid = {{ grid_size }};
|
||||||
const iy1 = shape.y1 / {{ grid_size }};
|
const type = getActiveType();
|
||||||
const ix2 = shape.x2 / {{ grid_size }};
|
|
||||||
const iy2 = shape.y2 / {{ grid_size }};
|
const modeX = axisMode(type, 'x');
|
||||||
|
const modeY = axisMode(type, 'y');
|
||||||
|
|
||||||
|
function normPoint(v, mode) {
|
||||||
|
return mode === 'grid' ? (v / grid) : v;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
ix1,
|
modeX, modeY,
|
||||||
iy1,
|
x1: normPoint(shape.x1, modeX),
|
||||||
ix2,
|
y1: normPoint(shape.y1, modeY),
|
||||||
iy2,
|
x2: normPoint(shape.x2, modeX),
|
||||||
|
y2: normPoint(shape.y2, modeY),
|
||||||
color: shape.color
|
color: shape.color
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -327,28 +379,52 @@ function redrawAll() {
|
||||||
function drawShape(shape) {
|
function drawShape(shape) {
|
||||||
if (!ctx) return;
|
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.save();
|
||||||
ctx.strokeStyle = shape.color || '#000000';
|
ctx.strokeStyle = shape.color || '#000000';
|
||||||
|
|
||||||
if (shape.type === 'rect') {
|
if (shape.type === 'rect' || shape.type === 'ellipse') {
|
||||||
const x = shape.ix * {{ grid_size }};
|
const x = toPxX(shape.x);
|
||||||
const y = shape.iy * {{ grid_size }};
|
const y = toPxY(shape.y);
|
||||||
const w = shape.iw * {{ grid_size }};
|
const w = toPxX(shape.w);
|
||||||
const h = shape.ih * {{ grid_size }};
|
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) {
|
if (shape.fill) {
|
||||||
ctx.globalAlpha = 0.15;
|
ctx.globalAlpha = 0.15;
|
||||||
ctx.fillStyle = shape.color;
|
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;
|
ctx.globalAlpha = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (shape.type === 'line') {
|
} else if (shape.type === 'line') {
|
||||||
const x1 = shape.ix1 * {{ grid_size }};
|
const x1 = toPxX(shape.x1);
|
||||||
const y1 = shape.iy1 * {{ grid_size }};
|
const y1 = toPxY(shape.y1);
|
||||||
const x2 = shape.ix2 * {{ grid_size }};
|
const x2 = toPxX(shape.x2);
|
||||||
const y2 = shape.iy2 * {{ grid_size }};
|
const y2 = toPxY(shape.y2);
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x1, y1);
|
ctx.moveTo(x1, y1);
|
||||||
|
|
@ -546,13 +622,20 @@ gridEl.addEventListener('pointermove', (e) => {
|
||||||
color: currentShape.color
|
color: currentShape.color
|
||||||
});
|
});
|
||||||
drawShape(previewLine);
|
drawShape(previewLine);
|
||||||
} else {
|
} else if (tool === 'filled' || tool === 'outline') {
|
||||||
const previewRect = normalizeRect({
|
const previewRect = normalizeRect({
|
||||||
...currentShape,
|
...currentShape,
|
||||||
x2: snapX,
|
x2: snapX,
|
||||||
y2: snapY
|
y2: snapY
|
||||||
});
|
});
|
||||||
drawShape(previewRect);
|
drawShape(previewRect);
|
||||||
|
} else if (tool === 'filledEllipse' || tool === 'outlineEllipse') {
|
||||||
|
const previewEllipse = normalizeEllipse({
|
||||||
|
...currentShape,
|
||||||
|
x2: snapX,
|
||||||
|
y2: snapY
|
||||||
|
});
|
||||||
|
drawShape(previewEllipse);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.setLineDash([]);
|
ctx.setLineDash([]);
|
||||||
|
|
@ -586,7 +669,7 @@ gridEl.addEventListener('pointerdown', (e) => {
|
||||||
y2: snapY,
|
y2: snapY,
|
||||||
color: selectedColor
|
color: selectedColor
|
||||||
};
|
};
|
||||||
} else {
|
} else if (tool === 'outline' || tool === 'filled') {
|
||||||
currentShape = {
|
currentShape = {
|
||||||
tool,
|
tool,
|
||||||
x1: snapX,
|
x1: snapX,
|
||||||
|
|
@ -596,6 +679,16 @@ gridEl.addEventListener('pointerdown', (e) => {
|
||||||
color: selectedColor,
|
color: selectedColor,
|
||||||
fill: document.getElementById('filled').checked
|
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') {
|
if (currentShape.tool === 'line') {
|
||||||
const line = normalizeLine(currentShape);
|
const line = normalizeLine(currentShape);
|
||||||
|
|
||||||
if (line.ix1 !== line.ix2 || line.iy1 !== line.iy2) {
|
if (line.x1 !== line.x2 || line.y1 !== line.y2) {
|
||||||
finalShape = line;
|
finalShape = line;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (currentShape.tool === 'filled' || currentShape.tool === 'outline') {
|
||||||
const rect = normalizeRect(currentShape);
|
const rect = normalizeRect(currentShape);
|
||||||
|
|
||||||
if (rect.iw > 0 && rect.ih > 0) {
|
if (rect.w > 0 && rect.h > 0) {
|
||||||
finalShape = rect;
|
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) {
|
if (finalShape) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue