diff --git a/web/rs_crop.js b/web/rs_crop.js index fd32f73..b19fdaf 100644 --- a/web/rs_crop.js +++ b/web/rs_crop.js @@ -4,48 +4,6 @@ import { api } from "../../scripts/api.js"; app.registerExtension({ name: "RaykoStudio.CropImage", - setup() { - api.addEventListener("rayko.rscrop.show", (event) => { - const { node_id, image_url, image_width, image_height } = event.detail; - const node = app.graph.getNodeById(node_id); - if (node) { - node.image.src = image_url + "&t=" + Date.now(); - node.image.onload = () => { - node.imageReady = true; - node.imageWidth = image_width || node.image.width; - node.imageHeight = image_height || node.image.height; - - const overlay = document.querySelector(`canvas[style*="z-index: 1000"]`); - if (overlay) { - overlay.style.display = "block"; - } - - if (node.properties?.crop_rect) { - try { - const r = JSON.parse(node.properties.crop_rect); - if (r.x >= node.imageWidth) r.x = 0; - if (r.y >= node.imageHeight) r.y = 0; - if (r.x + r.width > node.imageWidth) r.width = Math.max(64, node.imageWidth - r.x); - if (r.y + r.height > node.imageHeight) r.height = Math.max(64, node.imageHeight - r.y); - node.properties.crop_rect = JSON.stringify(r); - node._cropRect = r; - } catch (e) {} - } - - setTimeout(() => { - _lastRect = null; - syncPosition(); - }, 100); - node.setDirtyCanvas(true, true); - }; - if (node.size[1] < 650) node.setSize([node.size[0], 650]); - node.currentStatus = "🖱️ Drag to select crop area, then APPROVE!"; - app.canvas.centerOnNode(node); - node.setDirtyCanvas(true, true); - } - }); - }, - async beforeRegisterNodeDef(nodeType, nodeData, app) { if (nodeData.name !== "RSCropImage") return; @@ -61,8 +19,8 @@ app.registerExtension({ node.buttons = []; node.imageWidth = 0; node.imageHeight = 0; - node._cropRect = { x: 0, y: 0, width: 256, height: 256 }; + node._overlayCanvas = null; const cropDataWidget = node.widgets?.find(w => w.name === "crop_data"); if (cropDataWidget) { @@ -85,9 +43,9 @@ app.registerExtension({ node.setSize([500, 650]); - let _overlayCanvas = null; let _syncRunning = false; let _lastRect = null; + let _lastScale = 1; let _isSelecting = false; let _isDragging = false; let _isResizing = false; @@ -109,10 +67,12 @@ app.registerExtension({ node.properties.crop_rect = jsonStr; if (cropDataWidget) cropDataWidget.value = jsonStr; drawOverlay(); + node.setDirtyCanvas(true, true); }; + // ⚠️ ИСПРАВЛЕНО: Точно как в onDrawForeground const calculateImageRect = () => { - if (!app.canvas) return null; + if (!app.canvas || !node.imageReady) return null; const ds = app.canvas.ds; const canvasEl = app.canvas.canvas; @@ -139,8 +99,10 @@ app.registerExtension({ const footerHeight = 60; const outputSlotsHeight = 40; - const availableHeight = (node.size[1] * scale) - (titleBarHeight * scale) - (widgetsTotalHeight * scale) - (footerHeight * scale) - (padding * 2 * scale) - outputSlotsHeight; - const availableWidth = (node.size[0] * scale) - (padding * 2 * scale); + // ⚠️ Точно как в onDrawForeground + const startY = titleBarHeight + widgetsTotalHeight + padding + outputSlotsHeight; + const availableHeight = node.size[1] - startY - footerHeight - padding; + const availableWidth = node.size[0] - (padding * 2); if (availableWidth <= 0 || availableHeight <= 0) return null; @@ -148,7 +110,7 @@ app.registerExtension({ let drawH = availableHeight; let contentScale = 1; - if (node.imageReady && node.image && node.imageWidth > 0) { + if (node.imageWidth > 0 && node.imageHeight > 0) { const imgRatio = node.imageWidth / node.imageHeight; if (availableWidth / availableHeight > imgRatio) { @@ -162,14 +124,15 @@ app.registerExtension({ contentScale = drawW / node.imageWidth; } - const drawX = nodeScreenX + (padding * scale) + ((availableWidth - drawW) / 2); - const drawY = nodeScreenY + (titleBarHeight * scale) + (widgetsTotalHeight * scale) + (padding * scale) + ((availableHeight - drawH) / 2) + outputSlotsHeight; + // ⚠️ Конвертируем в экранные координаты + const drawX = nodeScreenX + (padding * scale) + ((availableWidth - drawW) * scale / 2); + const drawY = nodeScreenY + (startY * scale) + ((availableHeight - drawH) * scale / 2); return { left: drawX, top: drawY, - width: drawW, - height: drawH, + width: drawW * scale, + height: drawH * scale, scale: contentScale }; }; @@ -180,54 +143,60 @@ app.registerExtension({ const syncLoop = () => { if (!_syncRunning) return; syncPosition(); - if (_overlayCanvas) requestAnimationFrame(syncLoop); + if (node._overlayCanvas) requestAnimationFrame(syncLoop); else _syncRunning = false; }; requestAnimationFrame(syncLoop); }; const syncPosition = () => { - if (!_overlayCanvas) return; + if (!node._overlayCanvas) return; const imgRect = calculateImageRect(); if (!imgRect) return; + const currentScale = app.canvas.ds.scale; + const scaleChanged = Math.abs(_lastScale - currentScale) > 0.01; + const hasChanged = !_lastRect || Math.abs(_lastRect.left - imgRect.left) > 0.5 || Math.abs(_lastRect.top - imgRect.top) > 0.5 || Math.abs(_lastRect.width - imgRect.width) > 0.5 || - Math.abs(_lastRect.height - imgRect.height) > 0.5; + Math.abs(_lastRect.height - imgRect.height) > 0.5 || + scaleChanged; if (hasChanged) { + _lastScale = currentScale; _lastRect = { ...imgRect }; - _overlayCanvas.style.left = `${imgRect.left}px`; - _overlayCanvas.style.top = `${imgRect.top}px`; - _overlayCanvas.style.width = `${imgRect.width}px`; - _overlayCanvas.style.height = `${imgRect.height}px`; - _overlayCanvas.dataset.scale = imgRect.scale; + node._overlayCanvas.style.left = `${imgRect.left}px`; + node._overlayCanvas.style.top = `${imgRect.top}px`; + node._overlayCanvas.style.width = `${imgRect.width}px`; + node._overlayCanvas.style.height = `${imgRect.height}px`; + node._overlayCanvas.dataset.scale = imgRect.scale; + node._overlayCanvas.style.display = "block"; // ⚠️ Всегда показываем drawOverlay(); } }; const createOverlayCanvas = () => { - if (_overlayCanvas) return; - _overlayCanvas = document.createElement("canvas"); - _overlayCanvas.style.cssText = ` + if (node._overlayCanvas) return; + node._overlayCanvas = document.createElement("canvas"); + node._overlayCanvas.style.cssText = ` position: fixed !important; z-index: 1000 !important; pointer-events: auto !important; cursor: crosshair !important; background: transparent !important; touch-action: none; border: 1px dashed #00FF00 !important; box-sizing: border-box !important; display: none; `; - document.body.appendChild(_overlayCanvas); + document.body.appendChild(node._overlayCanvas); - _overlayCanvas.addEventListener("mousedown", (e) => { + node._overlayCanvas.addEventListener("mousedown", (e) => { e.preventDefault(); e.stopPropagation(); - const rect = _overlayCanvas.getBoundingClientRect(); + const rect = node._overlayCanvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; - const scale = parseFloat(_overlayCanvas.dataset.scale || "1"); + const scale = parseFloat(node._overlayCanvas.dataset.scale || "1"); const imgX = x / scale; const imgY = y / scale; @@ -266,16 +235,16 @@ app.registerExtension({ _startY = y; _startRect = { ...node._cropRect }; - _overlayCanvas.setPointerCapture(e.pointerId || 1); + node._overlayCanvas.setPointerCapture(e.pointerId || 1); }); - _overlayCanvas.addEventListener("mousemove", (e) => { + node._overlayCanvas.addEventListener("mousemove", (e) => { if (!_isSelecting && !_isDragging && !_isResizing) return; - const rect = _overlayCanvas.getBoundingClientRect(); + const rect = node._overlayCanvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; - const scale = parseFloat(_overlayCanvas.dataset.scale || "1"); + const scale = parseFloat(node._overlayCanvas.dataset.scale || "1"); const deltaX = (x - _startX) / scale; const deltaY = (y - _startY) / scale; @@ -335,31 +304,31 @@ app.registerExtension({ updateCropRect(); }); - _overlayCanvas.addEventListener("mouseup", (e) => { + node._overlayCanvas.addEventListener("mouseup", (e) => { _isSelecting = false; _isDragging = false; _isResizing = false; _resizeHandle = null; - _overlayCanvas.releasePointerCapture(e.pointerId || 1); + node._overlayCanvas.releasePointerCapture(e.pointerId || 1); }); startSyncLoop(); }; const drawOverlay = () => { - if (!_overlayCanvas || !_lastRect) return; - const width = parseFloat(_overlayCanvas.style.width || "0"); - const height = parseFloat(_overlayCanvas.style.height || "0"); + if (!node._overlayCanvas) return; + const width = parseFloat(node._overlayCanvas.style.width || "0"); + const height = parseFloat(node._overlayCanvas.style.height || "0"); if (width <= 0 || height <= 0) return; const dpr = window.devicePixelRatio || 1; - _overlayCanvas.width = width * dpr; - _overlayCanvas.height = height * dpr; - const ctx = _overlayCanvas.getContext("2d"); + node._overlayCanvas.width = width * dpr; + node._overlayCanvas.height = height * dpr; + const ctx = node._overlayCanvas.getContext("2d"); ctx.scale(dpr, dpr); ctx.clearRect(0, 0, width, height); - const scale = parseFloat(_overlayCanvas.dataset.scale || "1"); + const scale = parseFloat(node._overlayCanvas.dataset.scale || "1"); const cropX = node._cropRect.x * scale; const cropY = node._cropRect.y * scale; @@ -471,13 +440,11 @@ app.registerExtension({ const onResize = node.onResize; node.onResize = function(size) { if (onResize) onResize.apply(this, arguments); - _lastRect = null; }; const onMove = node.onMove; node.onMove = function() { if (onMove) onMove.apply(this, arguments); - _lastRect = null; }; node.onMouseMove = function(event, pos, graphPos) { @@ -551,9 +518,9 @@ app.registerExtension({ node.onRemoved = function() { _syncRunning = false; - if (_overlayCanvas) { - _overlayCanvas.remove(); - _overlayCanvas = null; + if (node._overlayCanvas) { + node._overlayCanvas.remove(); + node._overlayCanvas = null; } fetch("/rayko/rscrop/cleanup", { @@ -565,20 +532,59 @@ app.registerExtension({ }).catch(err => {}); }; - createOverlayCanvas(); - - const originalOnRendered = app.canvas.onRendered; - app.canvas.onRendered = function() { - if (originalOnRendered) originalOnRendered.apply(this, arguments); - if (_overlayCanvas && node.imageReady) { - _lastRect = null; - syncPosition(); + api.addEventListener("rayko.rscrop.show", (event) => { + const { node_id, image_url, image_width, image_height } = event.detail; + const currentId = node.id; + + if (String(node_id) === String(currentId)) { + node.image.src = image_url + "&t=" + Date.now(); + node.image.onload = () => { + console.log("[RS Crop 🦊] Image loaded, showing overlay"); + node.imageReady = true; + node.imageWidth = image_width || node.image.width; + node.imageHeight = image_height || node.image.height; + + // ⚠️ Показываем оверлей + if (node._overlayCanvas) { + node._overlayCanvas.style.display = "block"; + console.log("[RS Crop 🦊] Overlay display set to block"); + } + + if (node.properties?.crop_rect) { + try { + const r = JSON.parse(node.properties.crop_rect); + if (r.x >= node.imageWidth) r.x = 0; + if (r.y >= node.imageHeight) r.y = 0; + if (r.x + r.width > node.imageWidth) r.width = Math.max(64, node.imageWidth - r.x); + if (r.y + r.height > node.imageHeight) r.height = Math.max(64, node.imageHeight - r.y); + node.properties.crop_rect = JSON.stringify(r); + node._cropRect = r; + } catch (e) {} + } + + setTimeout(() => { + _lastRect = null; + _lastScale = 1; + syncPosition(); + node.setDirtyCanvas(true, true); + }, 100); + }; + + if (node.size[1] < 650) node.setSize([node.size[0], 650]); + node.currentStatus = "🖱️ Drag to select crop area, then APPROVE!"; + app.canvas.centerOnNode(node); + node.setDirtyCanvas(true, true); } - }; + }); + + createOverlayCanvas(); } catch (error) { console.error("[RS Crop 🦊] Critical Error:", error); } }; + }, + + setup() { } }); \ No newline at end of file