Web Dev Solutions

Catalin Mititiuc

From 5c1dfbca50d6809e4a9546472b1a4ec092f35358 Mon Sep 17 00:00:00 2001 From: Catalin Mititiuc Date: Mon, 1 Apr 2024 12:20:04 -0700 Subject: WIP: end movement, turn buttons --- index.html | 61 ++++++++++++---- index.js | 230 ++++++++++++++++++++++++++++++++++++++----------------------- style.css | 84 ++++++++++++++++------ 3 files changed, 254 insertions(+), 121 deletions(-) diff --git a/index.html b/index.html index cb82c12..986da99 100644 --- a/index.html +++ b/index.html @@ -143,19 +143,50 @@
- - - - + + + + + 1 + + + + + + + + + + + + + + - - + + + + + + - - + + + + + + + + + + + + + + @@ -183,10 +214,13 @@

+ Davion + - Davion
@@ -244,10 +278,13 @@

+ Liao + - Liao
diff --git a/index.js b/index.js index b316a24..f9e4ba8 100644 --- a/index.js +++ b/index.js @@ -64,9 +64,13 @@ const svgns = "http://www.w3.org/2000/svg", map = document.querySelector('rect#map'), hex = document.getElementById('point'), ptGrp = document.getElementById('points'), + cntrGrp = document.getElementById('counters'), settingsPanel = document.getElementById('panel'), recordSheetVisibility = document.querySelector('#content input[type="checkbox"].visible'); +const q = s => document.querySelector(s), + qA = s => document.querySelectorAll(s); + const { x: VIEWBOX_X, y: VIEWBOX_Y, width: VIEWBOX_WIDTH, height: VIEWBOX_HEIGHT } = svg.viewBox.baseVal; @@ -135,7 +139,7 @@ let info = document.getElementById('status'); return acc; }, {}); - let transform = `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) scale(${scale}) ` + let transform = `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) scale(${scale})`; target.style.transform = transform; }); @@ -177,27 +181,22 @@ POINTS.forEach((row, index) => row.forEach(([x, y]) => { {troopNumber, troopAllegiance} = selectedSoldier.dataset, selector = troopSelector(troopNumber, troopAllegiance); - let transProp = getComputedStyle(ptGrp).transform.match(/-?\d+\.?\d*/g), - mtx = new DOMMatrix(transProp || ''), - pt = new DOMPoint(point.x.baseVal.value, point.y.baseVal.value), - svgP = pt.matrixTransform(mtx); - info.querySelector('#hex-count').textContent = '-'; info.style.display = 'none'; ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class')); svg.querySelectorAll('.sight-line').forEach(el => el.remove()); - counter.setAttributeNS(null, 'cx', svgP.x); - counter.setAttributeNS(null, 'cy', svgP.y); - counter.setAttributeNS(null, 'r', '0.25in'); + counter.setAttributeNS(null, 'cx', cx); + counter.setAttributeNS(null, 'cy', cy); + counter.setAttributeNS(null, 'r', '5'); counter.dataset.troopNumber = troopNumber; counter.dataset.troopAllegiance = troopAllegiance; counter.dataset.x = point.dataset.x; counter.dataset.y = point.dataset.y; counter.classList.add('counter'); - text.setAttributeNS(null, 'x', svgP.x); - text.setAttributeNS(null, 'y', svgP.y); + text.setAttributeNS(null, 'x', cx); + text.setAttributeNS(null, 'y', cy); text.dataset.troopNumber = troopNumber; text.dataset.troopAllegiance = troopAllegiance; text.textContent = troopNumber; @@ -205,7 +204,7 @@ POINTS.forEach((row, index) => row.forEach(([x, y]) => { document.querySelectorAll(`.counter${selector}`).forEach(el => el.remove()); - counter.addEventListener('click', e => { + counter.addEventListener('dblclick', e => { let selectedSoldier = document.querySelector('.soldier-record.selected'); if (selectedSoldier) { @@ -223,40 +222,50 @@ POINTS.forEach((row, index) => row.forEach(([x, y]) => { } }); - counter.addEventListener('mouseenter', e => { - let selectedSoldier = document.querySelector('.soldier-record.selected'); + // counter.addEventListener('mouseenter', e => { + // let selectedSoldier = document.querySelector('.soldier-record.selected'); - if (selectedSoldier) { - let {troopNumber, troopAllegiance} = selectedSoldier.dataset, - selector = troopSelector(troopNumber, troopAllegiance), - source = document.querySelector(`circle.counter${selector}`), + // if (selectedSoldier) { + // let {troopNumber, troopAllegiance} = selectedSoldier.dataset, + // selector = troopSelector(troopNumber, troopAllegiance), + // source = document.querySelector(`circle.counter${selector}`), - // TODO: use isEqualNode() method instead - sourceAndTargetAreNotTheSame = [ - troopNumber != e.target.dataset.troopNumber, - troopAllegiance != e.target.dataset.troopAllegiance - ].some(el => el); + // // TODO: use isEqualNode() method instead + // sourceAndTargetAreNotTheSame = [ + // troopNumber != e.target.dataset.troopNumber, + // troopAllegiance != e.target.dataset.troopAllegiance + // ].some(el => el); - if (source && sourceAndTargetAreNotTheSame) { - let sightLine = document.createElementNS(svgns, 'line'); + // if (source && sourceAndTargetAreNotTheSame) { + // let sightLine = document.createElementNS(svgns, 'line'); - sightLine.classList.add('sight-line'); - sightLine.setAttributeNS(null, 'x1', source.getAttribute('cx')); - sightLine.setAttributeNS(null, 'y1', source.getAttribute('cy')); - sightLine.setAttributeNS(null, 'x2', e.target.getAttribute('cx')); - sightLine.setAttributeNS(null, 'y2', e.target.getAttribute('cy')); + // sightLine.classList.add('sight-line'); + // sightLine.setAttributeNS(null, 'x1', source.getAttribute('cx')); + // sightLine.setAttributeNS(null, 'y1', source.getAttribute('cy')); + // sightLine.setAttributeNS(null, 'x2', e.target.getAttribute('cx')); + // sightLine.setAttributeNS(null, 'y2', e.target.getAttribute('cy')); - svg.appendChild(sightLine); - } - } - }); + // svg.appendChild(sightLine); + // } + // } + // }); - counter.addEventListener('mouseleave', e => { - document.querySelectorAll('.sight-line').forEach(el => el.remove()); - }); + // counter.addEventListener('mouseleave', e => { + // document.querySelectorAll('.sight-line').forEach(el => el.remove()); + // }); + + // svg.insertBefore(counter, ptGrp); + + // let symbCtr = document.createElementNS(svgns, 'use'); + + // symbCtr.setAttributeNS(null, 'href', '#troop-counter'); + // symbCtr.setAttributeNS(null, 'x', cx); + // symbCtr.setAttributeNS(null, 'y', cy); - svg.insertBefore(counter, ptGrp); - svg.insertBefore(text, ptGrp); + // cntrGrp.appendChild(symbCtr); + + cntrGrp.appendChild(counter); + cntrGrp.appendChild(text); } }); @@ -271,6 +280,13 @@ POINTS.forEach((row, index) => row.forEach(([x, y]) => { let sl = svg.querySelector('.sight-line'); if (counter && (!sl || sl.classList.contains('active'))) { + if (sl) { + info.querySelector('#hex-count').textContent = '-'; + info.style.display = 'none'; + ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class')); + svg.querySelectorAll('.sight-line').forEach(el => el.remove()); + } + let source = ptGrp.querySelector(`use[data-x="${counter.dataset.x}"][data-y="${counter.dataset.y}"]`); let [x1, y1] = [source.x.baseVal.value, source.y.baseVal.value]; let [x2, y2] = [e.target.x.baseVal.value, e.target.y.baseVal.value]; @@ -288,7 +304,8 @@ POINTS.forEach((row, index) => row.forEach(([x, y]) => { sightLine.setAttributeNS(null, 'x2', svgX2); sightLine.setAttributeNS(null, 'y2', svgY2); - svg.insertBefore(sightLine, ptGrp); + // svg.insertBefore(sightLine, ptGrp); + document.getElementById('grid').appendChild(sightLine); let coords = [ counter.dataset.x, @@ -309,16 +326,16 @@ POINTS.forEach((row, index) => row.forEach(([x, y]) => { } }); - point.addEventListener('mouseout', e => { - let sl = svg.querySelector('.sight-line.active'); + // point.addEventListener('mouseout', e => { + // let sl = svg.querySelector('.sight-line.active'); - if (sl) { - info.querySelector('#hex-count').textContent = '-'; - info.style.display = 'none'; - ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class')); - svg.querySelectorAll('.sight-line').forEach(el => el.remove()); - } - }); + // if (sl) { + // info.querySelector('#hex-count').textContent = '-'; + // info.style.display = 'none'; + // ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class')); + // svg.querySelectorAll('.sight-line').forEach(el => el.remove()); + // } + // }); point.addEventListener('click', e => { let sl = svg.querySelector('.sight-line'); @@ -477,7 +494,7 @@ function linedraw(x1, y1, x2, y2) { for (let i = 0; i <= n; i++) { let lerp = axial_lerp(axial1.q, axial1.r, axial2.q, axial2.r, 1.0 / n * i), round = axial_round(lerp.q, lerp.r), - { x, y } = axial_to_evenr(round.q, round.r) + { x, y } = axial_to_evenr(round.q, round.r); results.push([x, y]); } @@ -485,22 +502,22 @@ function linedraw(x1, y1, x2, y2) { return results; } -map.addEventListener('mousemove', e => { - let boundingRect = e.target.getBoundingClientRect(); - let pointerX = e.clientX - boundingRect.left; - let pointerY = e.clientY - boundingRect.top; - let [maxXpx, maxYpx] = [e.target.width, e.target.height].map(v => v.baseVal.value); - - let activeFiringArc = document.querySelector('polygon.firing-arc.active'); +function positionFiringArc(e) { + activeFiringArc = document.querySelector('polygon.firing-arc.active'); // TODO: handle exactly horizontal and vertical lines if (activeFiringArc) { - let {x: x1px, y: y1px} = activeFiringArc.points[0]; + let board = document.getElementById('image-maps'), + { width, height } = board.getBBox(), + pt = new DOMPoint(e.clientX, e.clientY), + { x: pointerX, y: pointerY } = pt.matrixTransform(board.getScreenCTM().inverse()), + [maxXpx, maxYpx] = [width, height], + {x: x1px, y: y1px} = activeFiringArc.points[0]; let [x2px, y2px] = [ - pointerX / boundingRect.width * maxXpx, - pointerY / boundingRect.height * maxYpx + pointerX / width * maxXpx, + pointerY / height * maxYpx ]; let xDiff = x2px - x1px; @@ -595,7 +612,7 @@ map.addEventListener('mousemove', e => { activeFiringArc.setAttributeNS(null, 'points', points); } -}); +} document.querySelectorAll('.soldier-record').forEach(el => el.addEventListener('click', e => { @@ -639,15 +656,13 @@ document.querySelectorAll('.set-firing-arc').forEach(el => el.addEventListener(' if (counter) { let arcLayer = document.getElementById('firing-arcs'); - let firingArcPlacementListener = e => { - document.querySelectorAll('.firing-arc.active').forEach(el => el.classList.remove('active')); - document.querySelector('#point').style.display = ''; - map.removeEventListener('click', firingArcPlacementListener); - }; - - map.addEventListener('click', firingArcPlacementListener); + let grid = document.getElementById('grid'); + const transform = getComputedStyle(grid).transform.match(/-?\d+\.?\d*/g); + const pt = new DOMPoint(counter.cx.baseVal.value, counter.cy.baseVal.value); + const mtx = new DOMMatrix(transform); + let tPt = pt.matrixTransform(mtx); - let pivotPoint = [counter.cx.baseVal.value, counter.cy.baseVal.value]; + let pivotPoint = [tPt.x, tPt.y]; let firingArc = document.createElementNS(svgns, 'polygon'); firingArc.classList.add('firing-arc', 'active'); @@ -656,8 +671,17 @@ document.querySelectorAll('.set-firing-arc').forEach(el => el.addEventListener(' firingArc.dataset.size = e.target.dataset.size; firingArc.setAttributeNS(null, 'points', `${pivotPoint} ${pivotPoint} ${pivotPoint}`); - arcLayer.prepend(firingArc); + arcLayer.appendChild(firingArc); + let firingArcPlacementListener = e => { + document.querySelectorAll('.firing-arc.active').forEach(el => el.classList.remove('active')); + document.querySelector('#point').style.display = ''; + firingArc.removeEventListener('click', firingArcPlacementListener); + svg.removeEventListener('mousemove', positionFiringArc); + }; + + svg.addEventListener('mousemove', positionFiringArc); + firingArc.addEventListener('click', firingArcPlacementListener); document.querySelector('#point').style.display = 'none'; } } @@ -674,30 +698,25 @@ document.querySelectorAll('.clear-firing-arcs').forEach(el => svg.addEventListener('wheel', e => { e.preventDefault(); - // const pt = svg.createSVGPoint(); - const pt = new DOMPoint(e.clientX, e.clientY); - - // pt.x = e.clientX; - // pt.y = e.clientY; - - const svgP = pt.matrixTransform(svg.getScreenCTM().inverse()); + const pt = new DOMPoint(e.clientX, e.clientY), + svgP = pt.matrixTransform(svg.getScreenCTM().inverse()); - let { x, y, width, height } = svg.viewBox.baseVal; + let { x, y, width, height } = svg.viewBox.baseVal, - let widthDelta = width * 0.25; - let heightDelta = height * 0.25; + widthDelta = width * 0.25, + heightDelta = height * 0.25, - let xChange = (svgP.x - x) / width * widthDelta; - let yChange = (svgP.y - y) / height * heightDelta; + xChange = (svgP.x - x) / width * widthDelta, + yChange = (svgP.y - y) / height * heightDelta, - let widthChange = (1 - ((svgP.x - x) / width)) * widthDelta; - let heightChange = (1 - ((svgP.y - y) / height)) * heightDelta; + widthChange = (1 - ((svgP.x - x) / width)) * widthDelta, + heightChange = (1 - ((svgP.y - y) / height)) * heightDelta, - let newX = parseInt(e.deltaY < 0 ? x + xChange : x - xChange); - let newWidth = parseInt(e.deltaY < 0 ? width - xChange - widthChange : width + xChange + widthChange); + newX = parseInt(e.deltaY < 0 ? x + xChange : x - xChange), + newWidth = parseInt(e.deltaY < 0 ? width - xChange - widthChange : width + xChange + widthChange), - let newY = parseInt(e.deltaY < 0 ? y + yChange : y - yChange); - let newHeight = parseInt(e.deltaY < 0 ? height - yChange - heightChange : height + yChange + heightChange); + newY = parseInt(e.deltaY < 0 ? y + yChange : y - yChange), + newHeight = parseInt(e.deltaY < 0 ? height - yChange - heightChange : height + yChange + heightChange); // console.log('VIEWBOX_X', 'VIEWBOX_Y', VIEWBOX_X, VIEWBOX_Y); // console.log('VIEWBOX_WIDTH', 'VIEWBOX_HEIGHT', VIEWBOX_WIDTH, VIEWBOX_HEIGHT); @@ -727,6 +746,17 @@ svg.addEventListener('wheel', e => { svg.setAttributeNS(null, 'viewBox', vb); }); +ptGrp.addEventListener('mouseout', e => { + let sl = svg.querySelector('.sight-line'); + + if (sl && sl.classList.contains('active')) { + info.querySelector('#hex-count').textContent = '-'; + info.style.display = 'none'; + ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class')); + svg.querySelectorAll('.sight-line').forEach(el => el.remove()); + } +}); + svg.addEventListener('pointerdown', e => { const minPanDistanceThreshold = 5; @@ -772,6 +802,30 @@ svg.addEventListener('pointerdown', e => { svg.addEventListener('pointerup', pointerUp); }); +// svg.addEventListener('pointermove', e => { +// if (e.target.classList.contains('counter')) { +// let p = svg.querySelector(`use[data-x="${e.target.dataset.x}"][data-y="${e.target.dataset.y}"]`); +// p.classList.add('hover'); +// p.dispatchEvent(new MouseEvent('mouseover')); + +// let removeHover = e => { +// p.classList.remove('hover'); +// e.target.removeEventListener('pointerout', removeHover); +// }; + +// e.target.addEventListener('pointerout', removeHover); +// } +// }); + recordSheetVisibility.addEventListener('input', e => { localStorage.setItem('recordsVisibility', recordSheetVisibility.checked); +}); + +document.querySelector('.end-move').addEventListener('click', e => { + let selectedSoldier = document.querySelector('.soldier-record.selected'); + + if (selectedSoldier) { + selectedSoldier.classList.toggle('selected'); + selectedSoldier.classList.toggle('movement-ended'); + } }); \ No newline at end of file diff --git a/style.css b/style.css index 9954d29..9e481fc 100644 --- a/style.css +++ b/style.css @@ -16,18 +16,22 @@ svg { height: 100%; } -svg text { +svg image#numbers { + image-rendering: pixelated; +} + +/* svg text { user-select: none; font-size: 4px; fill: black; - /* stroke: black; */ + stroke: black; stroke-width: 0.2px; font-weight: bold; transform: translateY(6px); font-family: monospace; text-anchor: middle; - /* display: none; */ -} + display: none; +} */ div#status { position: absolute; @@ -65,6 +69,10 @@ div#content { border-bottom: 1px solid gray; } +#content #buttons { + line-height: 1.5em; +} + #content > div { display: none; } @@ -121,25 +129,31 @@ div#content { } svg > defs > #point { - fill: teal; - fill-opacity: 0.2; - stroke: black; - stroke-width: 0.5px; + fill: inherit; + fill-opacity: inherit; + stroke: inherit; + stroke-width: inherit; + stroke-opacity: inherit; } use[href="#point"] { opacity: 0; + fill: teal; + fill-opacity: 0.2; + stroke: black; + stroke-width: 0.5px; } -use[href="#point"]:hover { +use[href="#point"]:hover, use[href="#point"].hover { opacity: 1; + fill: orange; } use[href="#point"].active { opacity: 1; } -g#points { +g#grid { transform: translate(19px, 31px) scale(4); } @@ -174,10 +188,10 @@ image.map-scans { text-align: right; } -circle.counter { +/* circle.counter { stroke: transparent; stroke-width: 0.5in; -} +} */ circle.counter[data-troop-allegiance="liao"] { fill: green; @@ -187,15 +201,19 @@ circle.counter[data-troop-allegiance="davion"] { fill: red; } -text.counter { - font-size: 80px; +text.counter, #troop-counter text { + font-size: 12px; font-weight: bold; - stroke: black; + /* stroke: black; */ fill: white; - stroke-width: 2px; + /* stroke-width: 0.5px; */ font-family: sans-serif; cursor: default; + text-anchor: middle; + /* transform: translateY(25%); */ + transform: translateY(4px); pointer-events: none; + user-select: none; } rect#map { @@ -226,7 +244,8 @@ line.firing-arc { .sight-line { stroke: orangered; - stroke-width: 3px; + stroke-width: 0.5px; + pointer-events: none; } .soldier-record { @@ -236,6 +255,15 @@ line.firing-arc { background-color: white; } +.soldier-record.selected { + background-color: khaki; +} + +.soldier-record.movement-ended { + background-color: none; + opacity: 0.5; +} + image#img1 { transform: scale(3.41) rotate(-0.15deg); /* opacity: 0.33; */ @@ -258,10 +286,6 @@ img.logo { display: block; } -div.soldier-record.selected { - background-color: khaki; -} - rect#debug-view-box { /* stroke: red; stroke-width: 20px; */ @@ -269,6 +293,24 @@ rect#debug-view-box { fill-opacity: 0.2; } +#troop-counter > .outer { + fill: inherit; +} + +#troop-counter > .inner { + fill: red; +} + +use[href="#troop-counter"] { + transform: translate(-7.5px, -7.5px); + fill: transparent; + /* fill: orange; */ +} + +use[href="#troop-counter"]:hover { + fill: orange; +} + @media (width >= 1800px) { #record-sheet { flex-direction: row; -- cgit v1.2.3