Implement robust world-based positioning for Blend Mode menu

Redesigned the positioning system for the Blend Mode menu, inspired by the "Custom Output Area" logic. The menu now anchors precisely to the top-right corner of the viewport and stays in place during panning and zooming.
This commit is contained in:
Dariusz L
2025-07-30 13:06:01 +02:00
parent 03950b1787
commit e3cef041c9
8 changed files with 338 additions and 72 deletions

View File

@@ -44,6 +44,7 @@ export class CanvasInteractions {
this.canvas.viewport.zoom = newZoom;
this.canvas.viewport.x = worldCoords.x - (mouseBufferX / this.canvas.viewport.zoom);
this.canvas.viewport.y = worldCoords.y - (mouseBufferY / this.canvas.viewport.zoom);
this.canvas.onViewportChange?.();
}
renderAndSave(shouldSave = false) {
this.canvas.render();
@@ -87,6 +88,29 @@ export class CanvasInteractions {
this.canvas.canvas.addEventListener('drop', this.handleDrop.bind(this));
this.canvas.canvas.addEventListener('contextmenu', this.handleContextMenu.bind(this));
}
/**
* Sprawdza czy punkt znajduje się w obszarze któregokolwiek z zaznaczonych layerów
*/
isPointInSelectedLayers(worldX, worldY) {
for (const layer of this.canvas.canvasSelection.selectedLayers) {
if (!layer.visible)
continue;
const centerX = layer.x + layer.width / 2;
const centerY = layer.y + layer.height / 2;
// Przekształć punkt do lokalnego układu współrzędnych layera
const dx = worldX - centerX;
const dy = worldY - centerY;
const rad = -layer.rotation * Math.PI / 180;
const rotatedX = dx * Math.cos(rad) - dy * Math.sin(rad);
const rotatedY = dx * Math.sin(rad) + dy * Math.cos(rad);
// Sprawdź czy punkt jest wewnątrz prostokąta layera
if (Math.abs(rotatedX) <= layer.width / 2 &&
Math.abs(rotatedY) <= layer.height / 2) {
return true;
}
}
return false;
}
resetInteractionState() {
this.interaction.mode = 'none';
this.interaction.resizeHandle = null;
@@ -132,9 +156,10 @@ export class CanvasInteractions {
// 2. Inne przyciski myszy
if (e.button === 2) { // Prawy przycisk myszy
this.preventEventDefaults(e);
const clickedLayerResult = this.canvas.canvasLayers.getLayerAtPosition(coords.world.x, coords.world.y);
if (clickedLayerResult && this.canvas.canvasSelection.selectedLayers.includes(clickedLayerResult.layer)) {
this.canvas.canvasLayers.showBlendModeMenu(coords.view.x, coords.view.y);
// Sprawdź czy kliknięto w obszarze któregokolwiek z zaznaczonych layerów (niezależnie od przykrycia)
if (this.isPointInSelectedLayers(coords.world.x, coords.world.y)) {
// Nowa logika przekazuje tylko współrzędne świata, menu pozycjonuje się samo
this.canvas.canvasLayers.showBlendModeMenu(coords.world.x, coords.world.y);
}
return;
}
@@ -618,6 +643,7 @@ export class CanvasInteractions {
this.canvas.viewport.y -= dy / this.canvas.viewport.zoom;
this.interaction.panStart = { x: e.clientX, y: e.clientY };
this.canvas.render();
this.canvas.onViewportChange?.();
}
dragLayers(worldCoords) {
if (this.interaction.isAltPressed && !this.interaction.hasClonedInDrag && this.canvas.canvasSelection.selectedLayers.length > 0) {