From 3e3635b114bf73b62ed4c5372c8f96d1afa94023 Mon Sep 17 00:00:00 2001 From: Danil Boldyrev Date: Sat, 3 Jun 2023 19:24:05 +0300 Subject: Made the applyZoomAndPan function global for other extensions --- .../canvas-zoom-and-pan/javascript/zoom.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'extensions-builtin') diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index 41d9ddf4..504eb35e 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -103,7 +103,14 @@ onUiLoaded(async() => { const elements = await getElements(); - function applyZoomAndPan(targetElement, elemId) { + function applyZoomAndPan(elemId) { + const targetElement = gradioApp().querySelector(elemId); + + if (!targetElement) { + console.log("Element not found"); + return; + } + targetElement.style.transformOrigin = "0 0"; let [zoomLevel, panX, panY] = [1, 0, 0]; let fullScreenMode = false; @@ -558,7 +565,11 @@ onUiLoaded(async() => { gradioApp().addEventListener("mousemove", handleMoveByKey); } - applyZoomAndPan(elements.sketch, elementIDs.sketch); - applyZoomAndPan(elements.inpaint, elementIDs.inpaint); - applyZoomAndPan(elements.inpaintSketch, elementIDs.inpaintSketch); + applyZoomAndPan(elementIDs.sketch); + applyZoomAndPan(elementIDs.inpaint); + applyZoomAndPan(elementIDs.inpaintSketch); + + + // Make the function global so that other extensions can take advantage of this solution + window.applyZoomAndPan = applyZoomAndPan; }); -- cgit v1.2.1 From dc273f7473143b9f66f4d04cc5f4df1fca6defd8 Mon Sep 17 00:00:00 2001 From: Danil Boldyrev Date: Sun, 4 Jun 2023 01:18:27 +0300 Subject: Fixed the redmask bug --- .../canvas-zoom-and-pan/javascript/zoom.js | 68 +++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) (limited to 'extensions-builtin') diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index 504eb35e..af935220 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -14,6 +14,23 @@ function getActiveTab(elements, all = false) { } } +// Get tab ID +function getTabId(elements,elementIDs) { + const activeTab = getActiveTab(elements); + const tabIdLookup = { + Sketch: elementIDs.sketch, + "Inpaint sketch": elementIDs.inpaintSketch, + Inpaint: elementIDs.inpaint, + }; + return tabIdLookup[activeTab.innerText]; + } + + // Get Active main tab to prevent "Undo" on text2img from being disabled + function getActiveMainTab() { + const selectedTab = document.querySelector("#tabs .tab-nav button.selected"); + return selectedTab; + } + // Wait until opts loaded async function waitForOpts() { return new Promise(resolve => { @@ -63,6 +80,43 @@ function createHotkeyConfig(defaultHotkeysConfig, hotkeysConfigOpts) { return result; } + /** + * The restoreImgRedMask function displays a red mask around an image to indicate the aspect ratio. + * If the image display property is set to 'none', the mask breaks. To fix this, the function + * temporarily sets the display property to 'block' and then hides the mask again after 300 milliseconds + * to avoid breaking the canvas. Additionally, the function adjusts the mask to work correctly on + * very long images. + */ + + function restoreImgRedMask(elements,elementIDs) { + const mainTabId = getTabId(elements,elementIDs); + + if (!mainTabId) return; + + const mainTab = document.querySelector(mainTabId); + const img = mainTab.querySelector("img"); + const imageARPreview = document.querySelector("#imageARPreview"); + + if (!img || !imageARPreview) return; + + imageARPreview.style.transform = ""; + if (parseFloat(mainTab.style.width) > 865) { + const transformValues = mainTab.style.transform.match(/[-+]?[0-9]*\.?[0-9]+/g).map(Number); + const [posX, posY , zoom] = transformValues; + + imageARPreview.style.transformOrigin = "0 0" + imageARPreview.style.transform = `scale(${zoom})`; + } + + if (img.style.display !== "none") return; + + img.style.display = "block"; + + setTimeout(() => { + img.style.display = "none"; + }, 300); + } + // Main onUiLoaded(async() => { const hotkeysConfigOpts = await waitForOpts(); @@ -89,7 +143,8 @@ onUiLoaded(async() => { sketch: "#img2img_sketch", inpaint: "#img2maskimg", inpaintSketch: "#inpaint_sketch", - img2imgTabs: "#mode_img2img .tab-nav" + img2imgTabs: "#mode_img2img .tab-nav", + rangeGroup: "#img2img_column_size", }; async function getElements() { @@ -103,6 +158,17 @@ onUiLoaded(async() => { const elements = await getElements(); + // Apply functionality to the range inputs + const rangeInputs = elements.rangeGroup + ? elements.rangeGroup.querySelectorAll("input") + : [document.querySelector("#img2img_width input[type='range']"), document.querySelector("#img2img_height input[type='range']")]; + + rangeInputs.forEach((input) => { + if (input) { + input.addEventListener("input",() => restoreImgRedMask(elements,elementIDs)); + } + }); + function applyZoomAndPan(elemId) { const targetElement = gradioApp().querySelector(elemId); -- cgit v1.2.1 From 1a491783309215bfe2cfcb7c32ebd9ac2057c501 Mon Sep 17 00:00:00 2001 From: Danil Boldyrev Date: Sun, 4 Jun 2023 03:04:46 +0300 Subject: Made a function applyZoomAndPan isolated each instance Isolated each instance of applyZoomAndPan, now if you add another element to the page, they will work correctly --- .../canvas-zoom-and-pan/javascript/zoom.js | 210 +++++++++++---------- 1 file changed, 113 insertions(+), 97 deletions(-) (limited to 'extensions-builtin') diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index af935220..a6434743 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -1,5 +1,3 @@ -// Main - // Helper functions // Get active tab function getActiveTab(elements, all = false) { @@ -15,21 +13,23 @@ function getActiveTab(elements, all = false) { } // Get tab ID -function getTabId(elements,elementIDs) { +function getTabId(elements, elementIDs) { const activeTab = getActiveTab(elements); const tabIdLookup = { - Sketch: elementIDs.sketch, - "Inpaint sketch": elementIDs.inpaintSketch, - Inpaint: elementIDs.inpaint, + "Sketch": elementIDs.sketch, + "Inpaint sketch": elementIDs.inpaintSketch, + "Inpaint": elementIDs.inpaint }; return tabIdLookup[activeTab.innerText]; - } +} - // Get Active main tab to prevent "Undo" on text2img from being disabled - function getActiveMainTab() { - const selectedTab = document.querySelector("#tabs .tab-nav button.selected"); +// Get Active main tab to prevent "Undo" on text2img from being disabled +function getActiveMainTab() { + const selectedTab = gradioApp().querySelector( + "#tabs .tab-nav button.selected" + ); return selectedTab; - } +} // Wait until opts loaded async function waitForOpts() { @@ -80,43 +80,45 @@ function createHotkeyConfig(defaultHotkeysConfig, hotkeysConfigOpts) { return result; } - /** - * The restoreImgRedMask function displays a red mask around an image to indicate the aspect ratio. - * If the image display property is set to 'none', the mask breaks. To fix this, the function - * temporarily sets the display property to 'block' and then hides the mask again after 300 milliseconds - * to avoid breaking the canvas. Additionally, the function adjusts the mask to work correctly on - * very long images. - */ - - function restoreImgRedMask(elements,elementIDs) { - const mainTabId = getTabId(elements,elementIDs); - - if (!mainTabId) return; - - const mainTab = document.querySelector(mainTabId); - const img = mainTab.querySelector("img"); - const imageARPreview = document.querySelector("#imageARPreview"); - - if (!img || !imageARPreview) return; - - imageARPreview.style.transform = ""; - if (parseFloat(mainTab.style.width) > 865) { - const transformValues = mainTab.style.transform.match(/[-+]?[0-9]*\.?[0-9]+/g).map(Number); - const [posX, posY , zoom] = transformValues; - - imageARPreview.style.transformOrigin = "0 0" - imageARPreview.style.transform = `scale(${zoom})`; - } - - if (img.style.display !== "none") return; - - img.style.display = "block"; - - setTimeout(() => { - img.style.display = "none"; - }, 300); - } - +/** + * The restoreImgRedMask function displays a red mask around an image to indicate the aspect ratio. + * If the image display property is set to 'none', the mask breaks. To fix this, the function + * temporarily sets the display property to 'block' and then hides the mask again after 300 milliseconds + * to avoid breaking the canvas. Additionally, the function adjusts the mask to work correctly on + * very long images. + */ + +function restoreImgRedMask(elements, elementIDs) { + const mainTabId = getTabId(elements, elementIDs); + + if (!mainTabId) return; + + const mainTab = gradioApp().querySelector(mainTabId); + const img = mainTab.querySelector("img"); + const imageARPreview = gradioApp().querySelector("#imageARPreview"); + + if (!img || !imageARPreview) return; + + imageARPreview.style.transform = ""; + if (parseFloat(mainTab.style.width) > 865) { + const transformValues = mainTab.style.transform + .match(/[-+]?[0-9]*\.?[0-9]+/g) + .map(Number); + const [posX, posY, zoom] = transformValues; + + imageARPreview.style.transformOrigin = "0 0"; + imageARPreview.style.transform = `scale(${zoom})`; + } + + if (img.style.display !== "none") return; + + img.style.display = "block"; + + setTimeout(() => { + img.style.display = "none"; + }, 300); +} + // Main onUiLoaded(async() => { const hotkeysConfigOpts = await waitForOpts(); @@ -138,18 +140,19 @@ onUiLoaded(async() => { let isMoving = false; let mouseX, mouseY; + let activeElement; const elementIDs = { sketch: "#img2img_sketch", inpaint: "#img2maskimg", inpaintSketch: "#inpaint_sketch", img2imgTabs: "#mode_img2img .tab-nav", - rangeGroup: "#img2img_column_size", + rangeGroup: "#img2img_column_size" }; async function getElements() { const elements = await Promise.all( - Object.values(elementIDs).map(id => document.querySelector(id)) + Object.values(elementIDs).map(id => gradioApp().querySelector(id)) ); return Object.fromEntries( Object.keys(elementIDs).map((key, index) => [key, elements[index]]) @@ -157,17 +160,20 @@ onUiLoaded(async() => { } const elements = await getElements(); - - // Apply functionality to the range inputs - const rangeInputs = elements.rangeGroup - ? elements.rangeGroup.querySelectorAll("input") - : [document.querySelector("#img2img_width input[type='range']"), document.querySelector("#img2img_height input[type='range']")]; - - rangeInputs.forEach((input) => { - if (input) { - input.addEventListener("input",() => restoreImgRedMask(elements,elementIDs)); - } - }); + const elemData = {}; + + // Apply functionality to the range inputs. Restore redmask and correct for long images. + const rangeInputs = elements.rangeGroup ? elements.rangeGroup.querySelectorAll("input") : + [ + gradioApp().querySelector("#img2img_width input[type='range']"), + gradioApp().querySelector("#img2img_height input[type='range']") + ]; + + rangeInputs.forEach(input => { + if (input) { + input.addEventListener("input", () => restoreImgRedMask(elements, elementIDs)); + } + }); function applyZoomAndPan(elemId) { const targetElement = gradioApp().querySelector(elemId); @@ -178,7 +184,12 @@ onUiLoaded(async() => { } targetElement.style.transformOrigin = "0 0"; - let [zoomLevel, panX, panY] = [1, 0, 0]; + + elemData[elemId] = { + zoom: 1, + panX: 0, + panY: 0 + }; let fullScreenMode = false; // Create tooltip @@ -197,7 +208,7 @@ onUiLoaded(async() => { const tooltipContent = document.createElement("div"); tooltipContent.className = "tooltip-content"; - // Add info about hotkets + // Add info about hotkeys const zoomKey = hotkeysConfig.canvas_swap_controls ? "Ctrl" : "Shift"; const adjustKey = hotkeysConfig.canvas_swap_controls ? "Shift" : "Ctrl"; @@ -205,21 +216,15 @@ onUiLoaded(async() => { {key: `${zoomKey} + wheel`, action: "Zoom canvas"}, {key: `${adjustKey} + wheel`, action: "Adjust brush size"}, { - key: hotkeysConfig.canvas_hotkey_reset.charAt( - hotkeysConfig.canvas_hotkey_reset.length - 1 - ), + key: hotkeysConfig.canvas_hotkey_reset.charAt(hotkeysConfig.canvas_hotkey_reset.length - 1), action: "Reset zoom" }, { - key: hotkeysConfig.canvas_hotkey_fullscreen.charAt( - hotkeysConfig.canvas_hotkey_fullscreen.length - 1 - ), + key: hotkeysConfig.canvas_hotkey_fullscreen.charAt(hotkeysConfig.canvas_hotkey_fullscreen.length - 1), action: "Fullscreen mode" }, { - key: hotkeysConfig.canvas_hotkey_move.charAt( - hotkeysConfig.canvas_hotkey_move.length - 1 - ), + key: hotkeysConfig.canvas_hotkey_move.charAt(hotkeysConfig.canvas_hotkey_move.length - 1), action: "Move canvas" } ]; @@ -259,12 +264,14 @@ onUiLoaded(async() => { // Reset the zoom level and pan position of the target element to their initial values function resetZoom() { - zoomLevel = 1; - panX = 0; - panY = 0; + elemData[elemId] = { + zoomLevel: 1, + panX: 0, + panY: 0 + }; fixCanvas(); - targetElement.style.transform = `scale(${zoomLevel}) translate(${panX}px, ${panY}px)`; + targetElement.style.transform = `scale(${elemData[elemId].zoomLevel}) translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px)`; const canvas = gradioApp().querySelector( `${elemId} canvas[key="interface"]` @@ -342,11 +349,14 @@ onUiLoaded(async() => { // Update the zoom level and pan position of the target element based on the values of the zoomLevel, panX and panY variables function updateZoom(newZoomLevel, mouseX, mouseY) { newZoomLevel = Math.max(0.5, Math.min(newZoomLevel, 15)); - panX += mouseX - (mouseX * newZoomLevel) / zoomLevel; - panY += mouseY - (mouseY * newZoomLevel) / zoomLevel; + + elemData[elemId].panX += + mouseX - (mouseX * newZoomLevel) / elemData[elemId].zoomLevel; + elemData[elemId].panY += + mouseY - (mouseY * newZoomLevel) / elemData[elemId].zoomLevel; targetElement.style.transformOrigin = "0 0"; - targetElement.style.transform = `translate(${panX}px, ${panY}px) scale(${newZoomLevel})`; + targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${newZoomLevel})`; toggleOverlap("on"); return newZoomLevel; @@ -362,9 +372,9 @@ onUiLoaded(async() => { let zoomPosX, zoomPosY; let delta = 0.2; - if (zoomLevel > 7) { + if (elemData[elemId].zoomLevel > 7) { delta = 0.9; - } else if (zoomLevel > 2) { + } else if (elemData[elemId].zoomLevel > 2) { delta = 0.6; } @@ -372,8 +382,9 @@ onUiLoaded(async() => { zoomPosY = e.clientY; fullScreenMode = false; - zoomLevel = updateZoom( - zoomLevel + (operation === "+" ? delta : -delta), + elemData[elemId].zoomLevel = updateZoom( + elemData[elemId].zoomLevel + + (operation === "+" ? delta : -delta), zoomPosX - targetElement.getBoundingClientRect().left, zoomPosY - targetElement.getBoundingClientRect().top ); @@ -424,9 +435,9 @@ onUiLoaded(async() => { targetElement.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`; // Update global variables - zoomLevel = scale; - panX = offsetX; - panY = offsetY; + elemData[elemId].zoomLevel = scale; + elemData[elemId].panX = offsetX; + elemData[elemId].panY = offsetY; fullScreenMode = false; toggleOverlap("off"); @@ -500,9 +511,9 @@ onUiLoaded(async() => { targetElement.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`; // Update global variables - zoomLevel = scale; - panX = offsetX; - panY = offsetY; + elemData[elemId].zoomLevel = scale; + elemData[elemId].panX = offsetX; + elemData[elemId].panY = offsetY; fullScreenMode = true; toggleOverlap("on"); @@ -538,6 +549,8 @@ onUiLoaded(async() => { if (!isKeyDownHandlerAttached) { document.addEventListener("keydown", handleKeyDown); isKeyDownHandlerAttached = true; + + activeElement = elemId; } } @@ -545,6 +558,8 @@ onUiLoaded(async() => { if (isKeyDownHandlerAttached) { document.removeEventListener("keydown", handleKeyDown); isKeyDownHandlerAttached = false; + + activeElement = null; } } @@ -601,21 +616,23 @@ onUiLoaded(async() => { // Detect zoom level and update the pan speed. function updatePanPosition(movementX, movementY) { - let panSpeed = 1.5; + let panSpeed = 2; - if (zoomLevel > 8) { - panSpeed = 2.5; + if (elemData[elemId].zoomLevel > 8) { + panSpeed = 3.5; } - panX = panX + movementX * panSpeed; - panY = panY + movementY * panSpeed; + elemData[elemId].panX = + elemData[elemId].panX + movementX * panSpeed; + elemData[elemId].panY = + elemData[elemId].panY + movementY * panSpeed; - targetElement.style.transform = `translate(${panX}px, ${panY}px) scale(${zoomLevel})`; + targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${elemData[elemId].zoomLevel})`; toggleOverlap("on"); } function handleMoveByKey(e) { - if (isMoving) { + if (isMoving && elemId === activeElement) { updatePanPosition(e.movementX, e.movementY); targetElement.style.pointerEvents = "none"; } else { @@ -635,7 +652,6 @@ onUiLoaded(async() => { applyZoomAndPan(elementIDs.inpaint); applyZoomAndPan(elementIDs.inpaintSketch); - // Make the function global so that other extensions can take advantage of this solution window.applyZoomAndPan = applyZoomAndPan; }); -- cgit v1.2.1 From ad3d6d9a22ee8dc4e97c28f87d82b604bc7f5efe Mon Sep 17 00:00:00 2001 From: Danil Boldyrev Date: Sun, 4 Jun 2023 03:38:21 +0300 Subject: Fixed visual bugs --- extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'extensions-builtin') diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index a6434743..7ae2c635 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -622,13 +622,14 @@ onUiLoaded(async() => { panSpeed = 3.5; } - elemData[elemId].panX = - elemData[elemId].panX + movementX * panSpeed; - elemData[elemId].panY = - elemData[elemId].panY + movementY * panSpeed; + elemData[elemId].panX += movementX * panSpeed; + elemData[elemId].panY += movementY * panSpeed; - targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${elemData[elemId].zoomLevel})`; - toggleOverlap("on"); + // Delayed redraw of an element + requestAnimationFrame(() => { + targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${elemData[elemId].zoomLevel})`; + toggleOverlap("on"); + }); } function handleMoveByKey(e) { -- cgit v1.2.1 From 0432e37843cd99e48732dbe7cc6e635bfcce8ec3 Mon Sep 17 00:00:00 2001 From: Danil Boldyrev Date: Sun, 4 Jun 2023 04:17:55 +0300 Subject: Correct definition zoom level I changed the regular expression and now I always have to select scale from style.transfo --- .../canvas-zoom-and-pan/javascript/zoom.js | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'extensions-builtin') diff --git a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js index 7ae2c635..2a2ed999 100644 --- a/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js +++ b/extensions-builtin/canvas-zoom-and-pan/javascript/zoom.js @@ -23,14 +23,6 @@ function getTabId(elements, elementIDs) { return tabIdLookup[activeTab.innerText]; } -// Get Active main tab to prevent "Undo" on text2img from being disabled -function getActiveMainTab() { - const selectedTab = gradioApp().querySelector( - "#tabs .tab-nav button.selected" - ); - return selectedTab; -} - // Wait until opts loaded async function waitForOpts() { return new Promise(resolve => { @@ -101,10 +93,13 @@ function restoreImgRedMask(elements, elementIDs) { imageARPreview.style.transform = ""; if (parseFloat(mainTab.style.width) > 865) { - const transformValues = mainTab.style.transform - .match(/[-+]?[0-9]*\.?[0-9]+/g) - .map(Number); - const [posX, posY, zoom] = transformValues; + const transformString = mainTab.style.transform; + const scaleMatch = transformString.match(/scale\(([-+]?[0-9]*\.?[0-9]+)\)/); + let zoom = 1; // default zoom + + if (scaleMatch && scaleMatch[1]) { + zoom = Number(scaleMatch[1]); + } imageARPreview.style.transformOrigin = "0 0"; imageARPreview.style.transform = `scale(${zoom})`; @@ -116,7 +111,7 @@ function restoreImgRedMask(elements, elementIDs) { setTimeout(() => { img.style.display = "none"; - }, 300); + }, 400); } // Main -- cgit v1.2.1