More grid support.
This commit is contained in:
parent
87ec637ac1
commit
5cc47c4a81
1 changed files with 99 additions and 15 deletions
|
|
@ -7,10 +7,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#grid {
|
#grid {
|
||||||
|
{#
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(to right, #ccc 1px, transparent 1px),
|
linear-gradient(to right, #ccc 1px, transparent 1px),
|
||||||
linear-gradient(to bottom, #ccc 1px, transparent 1px);
|
linear-gradient(to bottom, #ccc 1px, transparent 1px);
|
||||||
background-size: var(--grid) var(--grid);
|
background-size: var(--grid) var(--grid);
|
||||||
|
#}
|
||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
height: 80vh;
|
height: 80vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -77,12 +79,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="vr mx-1"></div>
|
<div class="vr mx-1"></div>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<input type="radio" class="btn-check" name="gridType" id="fullGrid">
|
<input type="radio" class="btn-check" name="gridType" id="noGrid" checked>
|
||||||
<label for="fullGrid"
|
<label for="noGrid"
|
||||||
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">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||||
class="bi bi-border-all" viewBox="0 0 16 16">
|
class="bi bi-border" viewBox="0 0 16 16">
|
||||||
<path d="M0 0h16v16H0zm1 1v6.5h6.5V1zm7.5 0v6.5H15V1zM15 8.5H8.5V15H15zM7.5 15V8.5H1V15z" />
|
<path
|
||||||
|
d="M0 0h.969v.5H1v.469H.969V1H.5V.969H0zm2.844 1h-.938V0h.938zm1.875 0H3.78V0h.938v1zm1.875 0h-.938V0h.938zm.937 0V.969H7.5V.5h.031V0h.938v.5H8.5v.469h-.031V1zm2.813 0h-.938V0h.938zm1.875 0h-.938V0h.938zm1.875 0h-.938V0h.938zM15.5 1h-.469V.969H15V.5h.031V0H16v.969h-.5zM1 1.906v.938H0v-.938zm6.5.938v-.938h1v.938zm7.5 0v-.938h1v.938zM1 3.78v.938H0V3.78zm6.5.938V3.78h1v.938zm7.5 0V3.78h1v.938zM1 5.656v.938H0v-.938zm6.5.938v-.938h1v.938zm7.5 0v-.938h1v.938zM.969 8.5H.5v-.031H0V7.53h.5V7.5h.469v.031H1v.938H.969zm1.875 0h-.938v-1h.938zm1.875 0H3.78v-1h.938v1zm1.875 0h-.938v-1h.938zm1.875-.031V8.5H7.53v-.031H7.5V7.53h.031V7.5h.938v.031H8.5v.938zm1.875.031h-.938v-1h.938zm1.875 0h-.938v-1h.938zm1.875 0h-.938v-1h.938zm1.406 0h-.469v-.031H15V7.53h.031V7.5h.469v.031h.5v.938h-.5zM0 10.344v-.938h1v.938zm7.5 0v-.938h1v.938zm8.5-.938v.938h-1v-.938zM0 12.22v-.938h1v.938zm7.5 0v-.938h1v.938zm8.5-.938v.938h-1v-.938zM0 14.094v-.938h1v.938zm7.5 0v-.938h1v.938zm8.5-.938v.938h-1v-.938zM.969 16H0v-.969h.5V15h.469v.031H1v.469H.969zm1.875 0h-.938v-1h.938zm1.875 0H3.78v-1h.938v1zm1.875 0h-.938v-1h.938zm.937 0v-.5H7.5v-.469h.031V15h.938v.031H8.5v.469h-.031v.5zm2.813 0h-.938v-1h.938zm1.875 0h-.938v-1h.938zm1.875 0h-.938v-1h.938zm.937 0v-.5H15v-.469h.031V15h.469v.031h.5V16z" />
|
||||||
</svg>
|
</svg>
|
||||||
</label>
|
</label>
|
||||||
<input type="radio" class="btn-check" name="gridType" id="horizontalGrid">
|
<input type="radio" class="btn-check" name="gridType" id="horizontalGrid">
|
||||||
|
|
@ -103,13 +106,12 @@
|
||||||
d="M.969 0H0v.969h.5V1h.469V.969H1V.5H.969zm.937 1h.938V0h-.938zm1.875 0h.938V0H3.78v1zm1.875 0h.938V0h-.938zM8.5 16h-1V0h1zm.906-15h.938V0h-.938zm1.875 0h.938V0h-.938zm1.875 0h.938V0h-.938zm1.875 0h.469V.969h.5V0h-.969v.5H15v.469h.031zM1 2.844v-.938H0v.938zm14-.938v.938h1v-.938zM1 4.719V3.78H0v.938h1zm14-.938v.938h1V3.78h-1zM1 6.594v-.938H0v.938zm14-.938v.938h1v-.938zM.5 8.5h.469v-.031H1V7.53H.969V7.5H.5v.031H0v.938h.5zm1.406 0h.938v-1h-.938zm1.875 0h.938v-1H3.78v1zm1.875 0h.938v-1h-.938zm3.75 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875 0h.469v-.031h.5V7.53h-.5V7.5h-.469v.031H15v.938h.031zM0 9.406v.938h1v-.938zm16 .938v-.938h-1v.938zm-16 .937v.938h1v-.938zm16 .938v-.938h-1v.938zm-16 .937v.938h1v-.938zm16 .938v-.938h-1v.938zM0 16h.969v-.5H1v-.469H.969V15H.5v.031H0zm1.906 0h.938v-1h-.938zm1.875 0h.938v-1H3.78v1zm1.875 0h.938v-1h-.938zm3.75 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875-.5v.5H16v-.969h-.5V15h-.469v.031H15v.469z" />
|
d="M.969 0H0v.969h.5V1h.469V.969H1V.5H.969zm.937 1h.938V0h-.938zm1.875 0h.938V0H3.78v1zm1.875 0h.938V0h-.938zM8.5 16h-1V0h1zm.906-15h.938V0h-.938zm1.875 0h.938V0h-.938zm1.875 0h.938V0h-.938zm1.875 0h.469V.969h.5V0h-.969v.5H15v.469h.031zM1 2.844v-.938H0v.938zm14-.938v.938h1v-.938zM1 4.719V3.78H0v.938h1zm14-.938v.938h1V3.78h-1zM1 6.594v-.938H0v.938zm14-.938v.938h1v-.938zM.5 8.5h.469v-.031H1V7.53H.969V7.5H.5v.031H0v.938h.5zm1.406 0h.938v-1h-.938zm1.875 0h.938v-1H3.78v1zm1.875 0h.938v-1h-.938zm3.75 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875 0h.469v-.031h.5V7.53h-.5V7.5h-.469v.031H15v.938h.031zM0 9.406v.938h1v-.938zm16 .938v-.938h-1v.938zm-16 .937v.938h1v-.938zm16 .938v-.938h-1v.938zm-16 .937v.938h1v-.938zm16 .938v-.938h-1v.938zM0 16h.969v-.5H1v-.469H.969V15H.5v.031H0zm1.906 0h.938v-1h-.938zm1.875 0h.938v-1H3.78v1zm1.875 0h.938v-1h-.938zm3.75 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875-.5v.5H16v-.969h-.5V15h-.469v.031H15v.469z" />
|
||||||
</svg>
|
</svg>
|
||||||
</label>
|
</label>
|
||||||
<input type="radio" class="btn-check" name="gridType" id="noGrid">
|
<input type="radio" class="btn-check" name="gridType" id="fullGrid">
|
||||||
<label for="noGrid"
|
<label for="fullGrid"
|
||||||
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">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||||
class="bi bi-border" viewBox="0 0 16 16">
|
class="bi bi-border-all" viewBox="0 0 16 16">
|
||||||
<path
|
<path d="M0 0h16v16H0zm1 1v6.5h6.5V1zm7.5 0v6.5H15V1zM15 8.5H8.5V15H15zM7.5 15V8.5H1V15z" />
|
||||||
d="M0 0h.969v.5H1v.469H.969V1H.5V.969H0zm2.844 1h-.938V0h.938zm1.875 0H3.78V0h.938v1zm1.875 0h-.938V0h.938zm.937 0V.969H7.5V.5h.031V0h.938v.5H8.5v.469h-.031V1zm2.813 0h-.938V0h.938zm1.875 0h-.938V0h.938zm1.875 0h-.938V0h.938zM15.5 1h-.469V.969H15V.5h.031V0H16v.969h-.5zM1 1.906v.938H0v-.938zm6.5.938v-.938h1v.938zm7.5 0v-.938h1v.938zM1 3.78v.938H0V3.78zm6.5.938V3.78h1v.938zm7.5 0V3.78h1v.938zM1 5.656v.938H0v-.938zm6.5.938v-.938h1v.938zm7.5 0v-.938h1v.938zM.969 8.5H.5v-.031H0V7.53h.5V7.5h.469v.031H1v.938H.969zm1.875 0h-.938v-1h.938zm1.875 0H3.78v-1h.938v1zm1.875 0h-.938v-1h.938zm1.875-.031V8.5H7.53v-.031H7.5V7.53h.031V7.5h.938v.031H8.5v.938zm1.875.031h-.938v-1h.938zm1.875 0h-.938v-1h.938zm1.875 0h-.938v-1h.938zm1.406 0h-.469v-.031H15V7.53h.031V7.5h.469v.031h.5v.938h-.5zM0 10.344v-.938h1v.938zm7.5 0v-.938h1v.938zm8.5-.938v.938h-1v-.938zM0 12.22v-.938h1v.938zm7.5 0v-.938h1v.938zm8.5-.938v.938h-1v-.938zM0 14.094v-.938h1v.938zm7.5 0v-.938h1v.938zm8.5-.938v.938h-1v-.938zM.969 16H0v-.969h.5V15h.469v.031H1v.469H.969zm1.875 0h-.938v-1h.938zm1.875 0H3.78v-1h.938v1zm1.875 0h-.938v-1h.938zm.937 0v-.5H7.5v-.469h.031V15h.938v.031H8.5v.469h-.031v.5zm2.813 0h-.938v-1h.938zm1.875 0h-.938v-1h.938zm1.875 0h-.938v-1h.938zm.937 0v-.5H15v-.469h.031V15h.469v.031h.5V16z" />
|
|
||||||
</svg>
|
</svg>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -186,9 +188,16 @@ if (savedTool) {
|
||||||
setActiveTool(savedTool);
|
setActiveTool(savedTool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const savedType = localStorage.getItem('gridType');
|
||||||
|
if (savedType) {
|
||||||
|
setActiveType(savedType);
|
||||||
|
}
|
||||||
|
|
||||||
resizeAndSetupCanvas();
|
resizeAndSetupCanvas();
|
||||||
window.addEventListener('resize', resizeAndSetupCanvas);
|
window.addEventListener('resize', resizeAndSetupCanvas);
|
||||||
|
|
||||||
|
setGrid();
|
||||||
|
|
||||||
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';
|
||||||
|
|
@ -202,6 +211,19 @@ function setActiveTool(toolId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getActiveType() {
|
||||||
|
const checked = document.querySelector('input[name="gridType"]:checked');
|
||||||
|
return checked ? checked.id : 'noGrid';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActiveType(typeId) {
|
||||||
|
const el = document.getElementById(typeId);
|
||||||
|
if (el) {
|
||||||
|
el.checked = true;
|
||||||
|
localStorage.setItem('gridType', typeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function snapToGrid(x, y) {
|
function snapToGrid(x, y) {
|
||||||
const rect = gridEl.getBoundingClientRect();
|
const rect = gridEl.getBoundingClientRect();
|
||||||
const clampedX = Math.min(Math.max(x, rect.left), rect.right);
|
const clampedX = Math.min(Math.max(x, rect.left), rect.right);
|
||||||
|
|
@ -210,17 +232,31 @@ function snapToGrid(x, y) {
|
||||||
const localX = clampedX - rect.left;
|
const localX = clampedX - rect.left;
|
||||||
const localY = clampedY - rect.top;
|
const localY = clampedY - rect.top;
|
||||||
|
|
||||||
const maxIx = Math.floor(rect.width / {{ grid_size }});
|
const grid = {{ grid_size }};
|
||||||
const maxIy = Math.floor(rect.height / {{ grid_size }});
|
const maxIx = Math.floor(rect.width / grid);
|
||||||
|
const maxIy = Math.floor(rect.height / grid);
|
||||||
|
|
||||||
const ix = Math.min(Math.max(Math.round(localX / {{ grid_size }}), 0), maxIx);
|
const ix = Math.min(Math.max(Math.round(localX / grid), 0), maxIx);
|
||||||
const iy = Math.min(Math.max(Math.round(localY / {{ grid_size }}), 0), maxIy);
|
const iy = Math.min(Math.max(Math.round(localY / grid), 0), maxIy);
|
||||||
|
|
||||||
|
const type = getActiveType();
|
||||||
|
|
||||||
|
let snapX = localX;
|
||||||
|
let snapY = localY;
|
||||||
|
|
||||||
|
if (type === 'fullGrid' || type === 'verticalGrid') {
|
||||||
|
snapX = ix * grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'fullGrid' || type === 'horizontalGrid') {
|
||||||
|
snapY = iy * grid;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ix,
|
ix,
|
||||||
iy,
|
iy,
|
||||||
x: ix * {{ grid_size }},
|
x: snapX,
|
||||||
y: iy * {{ grid_size }}
|
y: snapY
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,6 +380,45 @@ function saveShapes() {
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setGrid() {
|
||||||
|
const gridSize = {{ grid_size }};
|
||||||
|
const type = getActiveType();
|
||||||
|
|
||||||
|
gridEl.style.backgroundImage = "";
|
||||||
|
gridEl.style.backgroundSize = "";
|
||||||
|
gridEl.style.boxShadow = "none";
|
||||||
|
|
||||||
|
if (type === 'fullGrid') {
|
||||||
|
gridEl.style.backgroundImage =
|
||||||
|
"linear-gradient(to right, #ccc 1px, transparent 1px)," +
|
||||||
|
"linear-gradient(to bottom, #ccc 1px, transparent 1px)";
|
||||||
|
gridEl.style.backgroundSize = `${gridSize}px ${gridSize}px`;
|
||||||
|
gridEl.style.boxShadow = "inset 0 0 0 1px #ccc"; // full frame
|
||||||
|
|
||||||
|
} else if (type === 'horizontalGrid') {
|
||||||
|
gridEl.style.backgroundImage =
|
||||||
|
"linear-gradient(to bottom, #ccc 1px, transparent 1px)";
|
||||||
|
gridEl.style.backgroundSize = `100% ${gridSize}px`;
|
||||||
|
|
||||||
|
// left + right borders only
|
||||||
|
gridEl.style.boxShadow =
|
||||||
|
"inset 1px 0 0 0 #ccc, inset -1px 0 0 0 #ccc";
|
||||||
|
|
||||||
|
} else if (type === 'verticalGrid') {
|
||||||
|
gridEl.style.backgroundImage =
|
||||||
|
"linear-gradient(to right, #ccc 1px, transparent 1px)";
|
||||||
|
gridEl.style.backgroundSize = `${gridSize}px 100%`;
|
||||||
|
|
||||||
|
// top + bottom borders only
|
||||||
|
gridEl.style.boxShadow =
|
||||||
|
"inset 0 1px 0 0 #ccc, inset 0 -1px 0 0 #ccc";
|
||||||
|
|
||||||
|
} else { // noGrid
|
||||||
|
gridEl.style.boxShadow = "inset 0 0 0 1px #ccc";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
document.querySelectorAll('input[name="tool"]').forEach(input => {
|
document.querySelectorAll('input[name="tool"]').forEach(input => {
|
||||||
input.addEventListener('change', () => {
|
input.addEventListener('change', () => {
|
||||||
if (input.checked) {
|
if (input.checked) {
|
||||||
|
|
@ -352,6 +427,15 @@ document.querySelectorAll('input[name="tool"]').forEach(input => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('input[name="gridType"]').forEach(input => {
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
if (input.checked) {
|
||||||
|
localStorage.setItem('gridType', input.id);
|
||||||
|
}
|
||||||
|
setGrid();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
importButtonEl.addEventListener('click', () => importEl.click());
|
importButtonEl.addEventListener('click', () => importEl.click());
|
||||||
|
|
||||||
importEl.addEventListener('change', (e) => {
|
importEl.addEventListener('change', (e) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue