More functional refactor
This commit is contained in:
parent
e7f7bf1f31
commit
7f0c19becd
@ -46,7 +46,7 @@ window.addEventListener('load', () => {
|
|||||||
const svgns = "http://www.w3.org/2000/svg",
|
const svgns = "http://www.w3.org/2000/svg",
|
||||||
recordSheetVisibility = document.querySelector('#content input[type="checkbox"].visible');
|
recordSheetVisibility = document.querySelector('#content input[type="checkbox"].visible');
|
||||||
|
|
||||||
new PanZoom(svg);
|
PanZoom(svg);
|
||||||
|
|
||||||
const distanceOutput = document.getElementById('status');
|
const distanceOutput = document.getElementById('status');
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import FiringArc from './firingArc.js';
|
import firingArc from './game/firingArc.js';
|
||||||
import SightLine from './sightLine.js';
|
import SightLine from './game/sightLine.js';
|
||||||
import Counter from './counter.js';
|
import Counter from './game/counter.js';
|
||||||
|
|
||||||
const svgns = "http://www.w3.org/2000/svg";
|
const svgns = "http://www.w3.org/2000/svg";
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ export default class Game {
|
|||||||
|
|
||||||
constructor(svg) {
|
constructor(svg) {
|
||||||
this.svg = svg;
|
this.svg = svg;
|
||||||
this.firingArc = new FiringArc(svg);
|
this.firingArc = new firingArc(svg);
|
||||||
this.sightLine = new SightLine(svg);
|
this.sightLine = new SightLine(svg);
|
||||||
this.counter = new Counter(svg);
|
this.counter = new Counter(svg);
|
||||||
|
|
||||||
|
@ -1,11 +1,25 @@
|
|||||||
const HORZ_POINT_DISTANCE = 1.005,
|
// https://www.redblobgames.com/grids/hexagons/
|
||||||
VERT_POINT_DISTANCE = Math.sqrt(3) * HORZ_POINT_DISTANCE / 2,
|
|
||||||
|
|
||||||
FIRING_ARC_SIZE = {
|
// Horizontal distance between hex centers is sqrt(3) * size. The vertical
|
||||||
'small': Math.atan(HORZ_POINT_DISTANCE / (6 * VERT_POINT_DISTANCE)),
|
// distance is 3 / 2 * size. When we calculate horzDist / vertDist, the size
|
||||||
'medium': Math.atan((HORZ_POINT_DISTANCE / 2) / VERT_POINT_DISTANCE),
|
// cancels out, leaving us with a unitless ratio of sqrt(3) / (3 / 2), or
|
||||||
'large': Math.atan((21 * HORZ_POINT_DISTANCE) / (6 * VERT_POINT_DISTANCE))
|
// 2 * sqrt(3) / 3.
|
||||||
};
|
|
||||||
|
const svgns = "http://www.w3.org/2000/svg",
|
||||||
|
horzToVertDistRatio = 2 * Math.sqrt(3) / 3,
|
||||||
|
|
||||||
|
arcSize = {
|
||||||
|
'small': Math.atan(horzToVertDistRatio / 6),
|
||||||
|
'medium': Math.atan(horzToVertDistRatio / 2),
|
||||||
|
'large': Math.atan(7 * horzToVertDistRatio / 2)
|
||||||
|
},
|
||||||
|
|
||||||
|
firingArcVisibility = {
|
||||||
|
davion: false,
|
||||||
|
liao: false
|
||||||
|
};
|
||||||
|
|
||||||
|
let svg;
|
||||||
|
|
||||||
function calculateAngle(xDiff, yDiff) {
|
function calculateAngle(xDiff, yDiff) {
|
||||||
yDiff = -yDiff;
|
yDiff = -yDiff;
|
||||||
@ -73,7 +87,7 @@ function position(e) {
|
|||||||
let yDiff = y2px - y1px;
|
let yDiff = y2px - y1px;
|
||||||
let angle = calculateAngle(xDiff, yDiff);
|
let angle = calculateAngle(xDiff, yDiff);
|
||||||
|
|
||||||
let arcAngle = FIRING_ARC_SIZE[activeFiringArc.dataset.size];
|
let arcAngle = arcSize[activeFiringArc.dataset.size];
|
||||||
let distance = Math.sqrt((x2px - x1px) ** 2 + (y2px - y1px) ** 2);
|
let distance = Math.sqrt((x2px - x1px) ** 2 + (y2px - y1px) ** 2);
|
||||||
let yDelta = distance * Math.cos(angle) * Math.tan(arcAngle);
|
let yDelta = distance * Math.cos(angle) * Math.tan(arcAngle);
|
||||||
let xDelta = distance * Math.sin(angle) * Math.tan(arcAngle);
|
let xDelta = distance * Math.sin(angle) * Math.tan(arcAngle);
|
||||||
@ -164,30 +178,30 @@ function position(e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class FiringArc {
|
function setDataAttrs({ dataset: { allegiance, number }}, el) {
|
||||||
#firingArcVisibility = {
|
el.dataset.allegiance = allegiance;
|
||||||
davion: false,
|
el.dataset.number = number;
|
||||||
liao: false
|
}
|
||||||
};
|
|
||||||
|
|
||||||
constructor(svg) {
|
function getClipPathId({ dataset: { allegiance, number }}) {
|
||||||
this.svg = svg;
|
return `clip-path-${allegiance}-${number}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
set(size, { dataset: { allegiance, number }}, { x, y }) {
|
function getUnclipped() {
|
||||||
const svgns = "http://www.w3.org/2000/svg";
|
return svg.querySelectorAll('#firing-arcs polygon:not([clip-path])');
|
||||||
|
};
|
||||||
|
|
||||||
let existingArcs = this.svg.querySelectorAll(
|
export default function (el) {
|
||||||
`#firing-arcs [data-number="${number}"][data-allegiance="${allegiance}"]`
|
svg = el;
|
||||||
);
|
|
||||||
|
|
||||||
existingArcs.forEach(el => el.remove());
|
this.set = function (size, counter, { x, y }) {
|
||||||
|
this.get(counter).forEach(fa => fa.remove());
|
||||||
|
|
||||||
let arcLayer = this.svg.querySelector('#shapes');
|
let arcLayer = svg.querySelector('#shapes');
|
||||||
let outlineLayer = this.svg.querySelector('#lines');
|
let outlineLayer = svg.querySelector('#lines');
|
||||||
let arcContainer = this.svg.querySelector('#firing-arcs');
|
let arcContainer = svg.querySelector('#firing-arcs');
|
||||||
|
|
||||||
let grid = this.svg.querySelector('.board');
|
let grid = svg.querySelector('.board');
|
||||||
const transform = getComputedStyle(grid).transform.match(/-?\d+\.?\d*/g);
|
const transform = getComputedStyle(grid).transform.match(/-?\d+\.?\d*/g);
|
||||||
const pt = new DOMPoint(x, y);
|
const pt = new DOMPoint(x, y);
|
||||||
const mtx = new DOMMatrix(transform);
|
const mtx = new DOMMatrix(transform);
|
||||||
@ -197,14 +211,12 @@ export default class FiringArc {
|
|||||||
let firingArc = document.createElementNS(svgns, 'polygon');
|
let firingArc = document.createElementNS(svgns, 'polygon');
|
||||||
let firingArcOutline = document.createElementNS(svgns, 'polygon');
|
let firingArcOutline = document.createElementNS(svgns, 'polygon');
|
||||||
|
|
||||||
firingArc.classList.add('firing-arc', 'active');
|
setDataAttrs(counter, firingArc);
|
||||||
firingArc.dataset.number = number;
|
|
||||||
firingArc.dataset.allegiance = allegiance;
|
|
||||||
firingArc.dataset.size = size;
|
firingArc.dataset.size = size;
|
||||||
|
firingArc.classList.add('firing-arc', 'active');
|
||||||
firingArc.setAttributeNS(null, 'points', `${pivotPoint} ${pivotPoint} ${pivotPoint}`);
|
firingArc.setAttributeNS(null, 'points', `${pivotPoint} ${pivotPoint} ${pivotPoint}`);
|
||||||
|
|
||||||
firingArcOutline.dataset.number = number;
|
setDataAttrs(counter, firingArcOutline);
|
||||||
firingArcOutline.dataset.allegiance = allegiance;
|
|
||||||
firingArcOutline.setAttributeNS(null, 'points', `${pivotPoint} ${pivotPoint} ${pivotPoint}`);
|
firingArcOutline.setAttributeNS(null, 'points', `${pivotPoint} ${pivotPoint} ${pivotPoint}`);
|
||||||
|
|
||||||
let clipShape = document.createElementNS(svgns, 'circle');
|
let clipShape = document.createElementNS(svgns, 'circle');
|
||||||
@ -213,9 +225,8 @@ export default class FiringArc {
|
|||||||
clipShape.setAttributeNS(null, 'r', 100);
|
clipShape.setAttributeNS(null, 'r', 100);
|
||||||
|
|
||||||
let clipPath = document.createElementNS(svgns, 'clipPath');
|
let clipPath = document.createElementNS(svgns, 'clipPath');
|
||||||
clipPath.setAttributeNS(null, 'id', `clip-path-${allegiance}-${number}`);
|
setDataAttrs(counter, clipPath);
|
||||||
clipPath.dataset.number = number;
|
clipPath.setAttributeNS(null, 'id', getClipPathId(counter));
|
||||||
clipPath.dataset.allegiance = allegiance;
|
|
||||||
clipPath.appendChild(clipShape);
|
clipPath.appendChild(clipShape);
|
||||||
|
|
||||||
arcContainer.appendChild(clipPath);
|
arcContainer.appendChild(clipPath);
|
||||||
@ -223,9 +234,9 @@ export default class FiringArc {
|
|||||||
outlineLayer.appendChild(firingArcOutline);
|
outlineLayer.appendChild(firingArcOutline);
|
||||||
|
|
||||||
let firingArcPlacementListener = e => {
|
let firingArcPlacementListener = e => {
|
||||||
this.svg.querySelectorAll('.firing-arc.active').forEach(el => el.classList.remove('active'));
|
svg.querySelectorAll('.firing-arc.active').forEach(el => el.classList.remove('active'));
|
||||||
grid.removeAttribute('style');
|
grid.removeAttribute('style');
|
||||||
this.svg.removeEventListener('mousemove', position);
|
svg.removeEventListener('mousemove', position);
|
||||||
firingArc.removeEventListener('click', firingArcPlacementListener);
|
firingArc.removeEventListener('click', firingArcPlacementListener);
|
||||||
firingArc.removeEventListener('contextmenu', cancelFiringArcPlacement);
|
firingArc.removeEventListener('contextmenu', cancelFiringArcPlacement);
|
||||||
};
|
};
|
||||||
@ -236,65 +247,58 @@ export default class FiringArc {
|
|||||||
firingArc.removeEventListener('click', firingArcPlacementListener);
|
firingArc.removeEventListener('click', firingArcPlacementListener);
|
||||||
firingArc.removeEventListener('contextmenu', cancelFiringArcPlacement);
|
firingArc.removeEventListener('contextmenu', cancelFiringArcPlacement);
|
||||||
|
|
||||||
let existingArcs = this.svg.querySelectorAll(
|
this.get(counter).forEach(fa => fa.remove());
|
||||||
`#firing-arcs [data-number="${number}"][data-allegiance="${allegiance}"]`
|
|
||||||
);
|
|
||||||
|
|
||||||
existingArcs.forEach(el => el.remove());
|
|
||||||
|
|
||||||
grid.removeAttribute('style');
|
grid.removeAttribute('style');
|
||||||
this.svg.removeEventListener('mousemove', position);
|
svg.removeEventListener('mousemove', position);
|
||||||
};
|
};
|
||||||
|
|
||||||
grid.style.pointerEvents = 'none';
|
grid.style.pointerEvents = 'none';
|
||||||
this.svg.addEventListener('mousemove', position);
|
svg.addEventListener('mousemove', position);
|
||||||
firingArc.addEventListener('click', firingArcPlacementListener);
|
firingArc.addEventListener('click', firingArcPlacementListener);
|
||||||
firingArc.addEventListener('contextmenu', cancelFiringArcPlacement);
|
firingArc.addEventListener('contextmenu', cancelFiringArcPlacement);
|
||||||
}
|
};
|
||||||
|
|
||||||
clear(allegiance) {
|
this.clear = function (allegiance) {
|
||||||
const selector = `#firing-arcs [data-allegiance="${allegiance}"]`;
|
const selector = `#firing-arcs [data-allegiance="${allegiance}"]`;
|
||||||
this.svg.querySelectorAll(selector).forEach(el => el.remove());
|
svg.querySelectorAll(selector).forEach(el => el.remove());
|
||||||
}
|
};
|
||||||
|
|
||||||
get({ dataset: { allegiance, number }}) {
|
this.get = function ({ dataset: { allegiance, number }}) {
|
||||||
return this.svg.querySelectorAll(`#firing-arcs polygon[data-number="${number}"][data-allegiance="${allegiance}"]`);
|
return svg.querySelectorAll(`#firing-arcs polygon[data-number="${number}"][data-allegiance="${allegiance}"]`);
|
||||||
}
|
};
|
||||||
|
|
||||||
getUnclipped() {
|
this.toggleVisibility = function (allegiance) {
|
||||||
return this.svg.querySelectorAll('#firing-arcs polygon:not([clip-path])');
|
const vis = firingArcVisibility[allegiance],
|
||||||
}
|
clipPaths = svg.querySelectorAll(`clipPath[data-allegiance="${allegiance}"]`);
|
||||||
|
|
||||||
toggleVisibility(allegiance) {
|
|
||||||
const vis = this.#firingArcVisibility[allegiance],
|
|
||||||
clipPaths = this.svg.querySelectorAll(`clipPath[data-allegiance="${allegiance}"]`);
|
|
||||||
|
|
||||||
clipPaths.forEach(cp => cp.style.display = !vis ? 'none' : '');
|
clipPaths.forEach(cp => cp.style.display = !vis ? 'none' : '');
|
||||||
this.#firingArcVisibility[allegiance] = !vis;
|
firingArcVisibility[allegiance] = !vis;
|
||||||
}
|
};
|
||||||
|
|
||||||
toggleCounterVisibility({ dataset: { number, allegiance }}, vis) {
|
this.toggleCounterVisibility = function ({ dataset: { number, allegiance }}, vis) {
|
||||||
const cp = this.svg.querySelector(`#clip-path-${allegiance}-${number}`),
|
const cp = svg.querySelector(`#clip-path-${allegiance}-${number}`),
|
||||||
display = vis ? 'none' : '';
|
display = vis ? 'none' : '';
|
||||||
|
|
||||||
if (cp) {
|
if (cp) {
|
||||||
cp.style.display = this.#firingArcVisibility[allegiance] ? 'none' : display;
|
cp.style.display = firingArcVisibility[allegiance] ? 'none' : display;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
clipAll() {
|
this.clipAll = function () {
|
||||||
let unclipped = this.getUnclipped();
|
console.log('clipall')
|
||||||
|
let unclipped = getUnclipped();
|
||||||
|
|
||||||
unclipped.forEach(el => {
|
unclipped.forEach(el => {
|
||||||
const { number, allegiance } = el.dataset,
|
const { number, allegiance } = el.dataset,
|
||||||
clipPathId = `clip-path-${allegiance}-${number}`,
|
clipPathId = `clip-path-${allegiance}-${number}`,
|
||||||
isVisible = this.#firingArcVisibility[allegiance];
|
isVisible = firingArcVisibility[allegiance];
|
||||||
|
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
this.svg.querySelector(`#${clipPathId}`).style.display = 'none';
|
svg.querySelector(`#${clipPathId}`).style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
el.setAttributeNS(null, 'clip-path', `url(#${clipPathId})`);
|
el.setAttributeNS(null, 'clip-path', `url(#${clipPathId})`);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
@ -1,36 +1,33 @@
|
|||||||
import { pan, zoom } from 'pan-zoom';
|
import { pan, zoom } from 'pan-zoom';
|
||||||
|
|
||||||
export default class PanZoom {
|
const storageKey = 'pan-zoom',
|
||||||
#storageKey = 'pan-zoom';
|
zoomFactor = 0.25;
|
||||||
#zoomFactor = 0.25;
|
|
||||||
|
|
||||||
constructor(svg) {
|
function restorePanZoomVal(svg) {
|
||||||
this.#restorePanZoomVal(svg);
|
const storedPanZoomVal = localStorage.getItem(storageKey);
|
||||||
this.#addEventListeners(svg);
|
|
||||||
this.#observePanZoomChanges(svg);
|
if (storedPanZoomVal) {
|
||||||
|
svg.style.transform = storedPanZoomVal;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#storePanZoomVal(transformMatrix) {
|
function addEventListeners(svg) {
|
||||||
localStorage.setItem(this.#storageKey, transformMatrix);
|
svg.addEventListener('wheel', e => zoom(svg, e, zoomFactor), { passive: false });
|
||||||
}
|
svg.addEventListener('pointerdown', e => pan(svg, e), { passive: false });
|
||||||
|
}
|
||||||
|
|
||||||
#observePanZoomChanges(svg) {
|
function storePanZoomVal(transformMatrix) {
|
||||||
const observer =
|
localStorage.setItem(storageKey, transformMatrix);
|
||||||
new MutationObserver(() => this.#storePanZoomVal(svg.style.transform));
|
}
|
||||||
|
|
||||||
observer.observe(svg, { attributeFilter: ['style'] });
|
function observePanZoomChanges(svg) {
|
||||||
}
|
const observer = new MutationObserver(() => storePanZoomVal(svg.style.transform));
|
||||||
|
|
||||||
#restorePanZoomVal(svg) {
|
observer.observe(svg, { attributeFilter: ['style'] });
|
||||||
const storedPanZoomVal = localStorage.getItem(this.#storageKey);
|
}
|
||||||
|
|
||||||
if (storedPanZoomVal) {
|
export default function (svg) {
|
||||||
svg.style.transform = storedPanZoomVal;
|
restorePanZoomVal(svg);
|
||||||
}
|
addEventListeners(svg);
|
||||||
}
|
observePanZoomChanges(svg);
|
||||||
|
}
|
||||||
#addEventListeners(svg) {
|
|
||||||
svg.addEventListener('wheel', e => zoom(svg, e, this.#zoomFactor), { passive: false });
|
|
||||||
svg.addEventListener('pointerdown', e => pan(svg, e), { passive: false });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user