mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 14:50:44 +00:00
migrate warship drag rectangle from canvas2D to DOM overlay
The shift+drag warship selection rectangle was drawn on a second offscreen canvas, blitted onto the main canvas2D context every frame via world-coord transform. It's a screen-space rectangle though, so none of that math was load-bearing. Replace with a `<div>` positioned via inline left/top/width/height in screen pixels. Same color tinting (player territoryColor lightened 0.2, dashed border at 0.85 alpha, fill at 0.06). pointer-events:none so it doesn't intercept the drag. Drops ~95 LOC of canvas2D drawing (renderSelectionBox, drawDashedLine, selectionBoxCanvas/Ctx, the redraw() init, the renderLayer() blit). One step closer to retiring the canvas2D map canvas — UILayer's per-unit selection outlines are the last canvas2D draws on it.
This commit is contained in:
@@ -54,15 +54,9 @@ export class UILayer implements Layer {
|
||||
// Visual settings for selection
|
||||
private readonly SELECTION_BOX_SIZE = 6; // Size of the selection box (should be larger than the warship)
|
||||
|
||||
// Selection box (drag rectangle) state
|
||||
private selectionBoxActive = false;
|
||||
private selectionBoxStartX = 0;
|
||||
private selectionBoxStartY = 0;
|
||||
private selectionBoxEndX = 0;
|
||||
private selectionBoxEndY = 0;
|
||||
private selectionBoxCanvas: HTMLCanvasElement =
|
||||
document.createElement("canvas");
|
||||
private selectionBoxCtx: CanvasRenderingContext2D | null = null;
|
||||
// Drag rectangle (shift+drag warship selection box) — a screen-space DOM
|
||||
// overlay positioned via inline style. Not part of the canvas2D draw path.
|
||||
private dragRectEl: HTMLDivElement | null = null;
|
||||
|
||||
constructor(
|
||||
private game: GameView,
|
||||
@@ -106,22 +100,12 @@ export class UILayer implements Layer {
|
||||
|
||||
init() {
|
||||
this.eventBus.on(UnitSelectionEvent, (e) => this.onUnitSelection(e));
|
||||
|
||||
this.ensureDragRectEl();
|
||||
this.eventBus.on(WarshipSelectionBoxUpdateEvent, (e) => {
|
||||
this.selectionBoxActive = true;
|
||||
this.selectionBoxStartX = e.startX;
|
||||
this.selectionBoxStartY = e.startY;
|
||||
this.selectionBoxEndX = e.endX;
|
||||
this.selectionBoxEndY = e.endY;
|
||||
this.updateDragRect(e.startX, e.startY, e.endX, e.endY);
|
||||
});
|
||||
const clearBox = () => {
|
||||
this.selectionBoxActive = false;
|
||||
this.selectionBoxCtx?.clearRect(
|
||||
0,
|
||||
0,
|
||||
this.selectionBoxCanvas.width,
|
||||
this.selectionBoxCanvas.height,
|
||||
);
|
||||
};
|
||||
const clearBox = () => this.hideDragRect();
|
||||
this.eventBus.on(WarshipSelectionBoxCompleteEvent, clearBox);
|
||||
this.eventBus.on(WarshipSelectionBoxCancelEvent, clearBox);
|
||||
this.eventBus.on(CloseViewEvent, clearBox);
|
||||
@@ -137,6 +121,62 @@ export class UILayer implements Layer {
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily create the shift+drag rectangle overlay. Screen-space DOM element,
|
||||
* pointer-events: none so it doesn't intercept the drag itself. z-index
|
||||
* sits above the WebGL/canvas2D map canvases but below HUD modals.
|
||||
*/
|
||||
private ensureDragRectEl(): void {
|
||||
if (this.dragRectEl !== null) return;
|
||||
const el = document.createElement("div");
|
||||
el.id = "warship-drag-rect";
|
||||
el.style.position = "fixed";
|
||||
el.style.pointerEvents = "none";
|
||||
el.style.display = "none";
|
||||
el.style.zIndex = "30";
|
||||
el.style.borderStyle = "dashed";
|
||||
el.style.borderWidth = "1px";
|
||||
el.style.boxSizing = "border-box";
|
||||
document.body.appendChild(el);
|
||||
this.dragRectEl = el;
|
||||
}
|
||||
|
||||
private updateDragRect(
|
||||
startX: number,
|
||||
startY: number,
|
||||
endX: number,
|
||||
endY: number,
|
||||
): void {
|
||||
const el = this.dragRectEl;
|
||||
if (el === null) return;
|
||||
const x1 = Math.min(startX, endX);
|
||||
const y1 = Math.min(startY, endY);
|
||||
const w = Math.abs(endX - startX);
|
||||
const h = Math.abs(endY - startY);
|
||||
|
||||
// Color from the local player's territory tint (matches the canvas2D look).
|
||||
const myPlayer = this.game.myPlayer();
|
||||
const base = myPlayer ? myPlayer.territoryColor().lighten(0.2) : null;
|
||||
const border = base
|
||||
? base.alpha(0.85).toRgbString()
|
||||
: "rgba(100, 200, 255, 0.85)";
|
||||
const fill = base
|
||||
? base.alpha(0.06).toRgbString()
|
||||
: "rgba(100, 200, 255, 0.06)";
|
||||
|
||||
el.style.left = `${x1}px`;
|
||||
el.style.top = `${y1}px`;
|
||||
el.style.width = `${w}px`;
|
||||
el.style.height = `${h}px`;
|
||||
el.style.borderColor = border;
|
||||
el.style.backgroundColor = fill;
|
||||
el.style.display = "block";
|
||||
}
|
||||
|
||||
private hideDragRect(): void {
|
||||
if (this.dragRectEl !== null) this.dragRectEl.style.display = "none";
|
||||
}
|
||||
|
||||
/**
|
||||
* Find player-owned warships near the given cell, sorted by distance.
|
||||
*/
|
||||
@@ -298,89 +338,6 @@ export class UILayer implements Layer {
|
||||
this.game.width(),
|
||||
this.game.height(),
|
||||
);
|
||||
if (this.selectionBoxActive) {
|
||||
this.renderSelectionBox(context);
|
||||
}
|
||||
}
|
||||
|
||||
private renderSelectionBox(context: CanvasRenderingContext2D) {
|
||||
if (!this.selectionBoxCtx) return;
|
||||
|
||||
const topLeft = this.transformHandler.screenToWorldCoordinates(
|
||||
Math.min(this.selectionBoxStartX, this.selectionBoxEndX),
|
||||
Math.min(this.selectionBoxStartY, this.selectionBoxEndY),
|
||||
);
|
||||
const bottomRight = this.transformHandler.screenToWorldCoordinates(
|
||||
Math.max(this.selectionBoxStartX, this.selectionBoxEndX),
|
||||
Math.max(this.selectionBoxStartY, this.selectionBoxEndY),
|
||||
);
|
||||
|
||||
const cx1 = Math.max(0, Math.floor(topLeft.x));
|
||||
const cy1 = Math.max(0, Math.floor(topLeft.y));
|
||||
const cx2 = Math.min(
|
||||
this.selectionBoxCanvas.width - 1,
|
||||
Math.floor(bottomRight.x),
|
||||
);
|
||||
const cy2 = Math.min(
|
||||
this.selectionBoxCanvas.height - 1,
|
||||
Math.floor(bottomRight.y),
|
||||
);
|
||||
|
||||
if (cx2 <= cx1 || cy2 <= cy1) return;
|
||||
|
||||
const myPlayer = this.game.myPlayer();
|
||||
const baseColor = myPlayer ? myPlayer.territoryColor().lighten(0.2) : null;
|
||||
const colorStr = baseColor
|
||||
? baseColor.alpha(0.85).toRgbString()
|
||||
: "rgba(100,200,255,0.85)";
|
||||
|
||||
this.selectionBoxCtx.clearRect(
|
||||
0,
|
||||
0,
|
||||
this.selectionBoxCanvas.width,
|
||||
this.selectionBoxCanvas.height,
|
||||
);
|
||||
this.selectionBoxCtx.fillStyle = colorStr;
|
||||
this.drawDashedLine(this.selectionBoxCtx, cx1, cy1, cx2, cy1);
|
||||
this.drawDashedLine(this.selectionBoxCtx, cx1, cy2, cx2, cy2);
|
||||
this.drawDashedLine(this.selectionBoxCtx, cx1, cy1, cx1, cy2);
|
||||
this.drawDashedLine(this.selectionBoxCtx, cx2, cy1, cx2, cy2);
|
||||
|
||||
this.selectionBoxCtx.fillStyle = baseColor
|
||||
? baseColor.alpha(0.06).toRgbString()
|
||||
: "rgba(100,200,255,0.06)";
|
||||
this.selectionBoxCtx.fillRect(
|
||||
cx1 + 1,
|
||||
cy1 + 1,
|
||||
cx2 - cx1 - 1,
|
||||
cy2 - cy1 - 1,
|
||||
);
|
||||
|
||||
context.drawImage(
|
||||
this.selectionBoxCanvas,
|
||||
-this.game.width() / 2,
|
||||
-this.game.height() / 2,
|
||||
this.game.width(),
|
||||
this.game.height(),
|
||||
);
|
||||
}
|
||||
|
||||
private drawDashedLine(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
x1: number,
|
||||
y1: number,
|
||||
x2: number,
|
||||
y2: number,
|
||||
) {
|
||||
if (x1 === x2) {
|
||||
for (let y = y1; y <= y2; y++) {
|
||||
if ((x1 + y) % 2 === 0) ctx.fillRect(x1, y, 1, 1);
|
||||
}
|
||||
} else {
|
||||
for (let x = x1; x <= x2; x++) {
|
||||
if ((x + y1) % 2 === 0) ctx.fillRect(x, y1, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redraw() {
|
||||
@@ -388,11 +345,6 @@ export class UILayer implements Layer {
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.canvas.width = this.game.width();
|
||||
this.canvas.height = this.game.height();
|
||||
|
||||
this.selectionBoxCanvas = document.createElement("canvas");
|
||||
this.selectionBoxCanvas.width = this.game.width();
|
||||
this.selectionBoxCanvas.height = this.game.height();
|
||||
this.selectionBoxCtx = this.selectionBoxCanvas.getContext("2d");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user