{"id":313,"date":"2025-08-29T09:38:50","date_gmt":"2025-08-29T09:38:50","guid":{"rendered":"https:\/\/iotnoob.com\/wordpress\/?p=313"},"modified":"2025-08-30T02:59:37","modified_gmt":"2025-08-30T02:59:37","slug":"draggable-svg-object","status":"publish","type":"post","link":"https:\/\/iotnoob.com\/wordpress\/2025\/08\/29\/draggable-svg-object\/","title":{"rendered":"Draggable svg object"},"content":{"rendered":"\n<p>\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23 \u0e2a\u0e23\u0e49\u0e32\u0e07 \u0e2b\u0e23\u0e37\u0e2d \u0e41\u0e01\u0e49\u0e44\u0e02 \u0e20\u0e32\u0e1e SVG \u0e42\u0e14\u0e22\u0e40\u0e23\u0e32\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16\u0e17\u0e33\u0e01\u0e32\u0e23 add text \u0e41\u0e25\u0e30\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16 \u0e04\u0e25\u0e34\u0e01\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e40\u0e25\u0e37\u0e48\u0e2d\u0e19 Object \u0e17\u0e35\u0e48\u0e40\u0e23\u0e32\u0e40\u0e2b\u0e47\u0e19\u0e43\u0e19\u0e44\u0e1f\u0e25\u0e4c\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e\u0e19\u0e32\u0e21\u0e2a\u0e01\u0e38\u0e25 SVG \u0e44\u0e1b\u0e22\u0e31\u0e07\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e15\u0e48\u0e32\u0e07\u0e46\u0e44\u0e14\u0e49<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"422\" src=\"https:\/\/iotnoob.com\/wordpress\/wp-content\/uploads\/2025\/08\/image-100-1024x422.png\" alt=\"\" class=\"wp-image-315\" srcset=\"https:\/\/iotnoob.com\/wordpress\/wp-content\/uploads\/2025\/08\/image-100-1024x422.png 1024w, https:\/\/iotnoob.com\/wordpress\/wp-content\/uploads\/2025\/08\/image-100-300x124.png 300w, https:\/\/iotnoob.com\/wordpress\/wp-content\/uploads\/2025\/08\/image-100-768x317.png 768w, https:\/\/iotnoob.com\/wordpress\/wp-content\/uploads\/2025\/08\/image-100.png 1497w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<ol class=\"wp-block-list\">\n<li>user \u0e40\u0e25\u0e37\u0e2d\u0e01\u0e44\u0e1f\u0e25\u0e4c\u0e20\u0e32\u0e1e SVG \u0e21\u0e31\u0e19\u0e08\u0e30\u0e16\u0e39\u0e01\u0e19\u0e33\u0e02\u0e36\u0e49\u0e19\u0e21\u0e32\u0e41\u0e2a\u0e14\u0e07\u0e17\u0e35\u0e48\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\u0e14\u0e49\u0e32\u0e19\u0e02\u0e27\u0e32<\/li>\n\n\n\n<li>user \u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16 add text \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e41\u0e17\u0e23\u0e01\u0e15\u0e31\u0e27\u0e2d\u0e31\u0e01\u0e29\u0e23\u0e17\u0e35\u0e48\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e25\u0e07\u0e43\u0e19\u0e20\u0e32\u0e1e<\/li>\n\n\n\n<li>user \u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16\u0e04\u0e25\u0e34\u0e01\u0e1a\u0e19 text \u0e17\u0e35\u0e48\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e02\u0e36\u0e49\u0e19\u0e21\u0e32\u0e41\u0e25\u0e49\u0e27\u0e25\u0e32\u0e01\u0e21\u0e31\u0e19\u0e44\u0e1b\u0e27\u0e32\u0e07\u0e17\u0e35\u0e48\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e17\u0e35\u0e48\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23<\/li>\n\n\n\n<li>user \u0e01\u0e14 download \u0e40\u0e1e\u0e37\u0e48\u0e2d save \u0e20\u0e32\u0e1e\u0e17\u0e35\u0e48\u0e1c\u0e48\u0e32\u0e19\u0e01\u0e32\u0e23\u0e41\u0e01\u0e49\u0e44\u0e02 \u0e25\u0e07\u0e40\u0e1b\u0e47\u0e19\u0e44\u0e1f\u0e25\u0e4c\u0e43\u0e2b\u0e21\u0e48<\/li>\n\n\n\n<li>user \u0e01\u0e14 reset and clear \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e25\u0e1a\u0e20\u0e32\u0e1e\u0e2d\u0e2d\u0e01\u0e08\u0e32\u0e01\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\u0e41\u0e2a\u0e14\u0e07\u0e1c\u0e25<\/li>\n<\/ol>\n\n\n\n<p>HTML \u0e2a\u0e48\u0e27\u0e19 control button<br>\u0e21\u0e35 control \u0e2d\u0e22\u0e39\u0e48\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 1 file button + 3 buttons + 1 textbox<br>1. file button -> id = svg-upload<br>2. button -> id = add-text-btn<br>3. text -> id = text-input<br>4. button -> id = download-btn<br>5. button -> id = reset-btn<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">            &lt;!-- Controls Column -->\n            &lt;aside class=\"lg:col-span-1 bg-white p-6 rounded-lg shadow-lg h-fit\">\n                &lt;h2 class=\"text-2xl font-bold mb-4 border-b pb-2\">Controls&lt;\/h2>\n                \n                &lt;!-- File Upload -->\n                &lt;div class=\"mb-6\">\n                    &lt;label for=\"svg-upload\" class=\"block text-sm font-medium text-slate-700 mb-2\">1. Load SVG File&lt;\/label>\n                    &lt;input type=\"file\" id=\"svg-upload\" accept=\".svg,image\/svg+xml\" class=\"block w-full text-sm text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100\"\/>\n                    &lt;p class=\"mt-1 text-xs text-slate-500\">Elements with class=\"draggable\" will be movable.&lt;\/p>\n                &lt;\/div>\n\n                &lt;!-- Add Text -->\n                &lt;div class=\"mb-6\">\n                    &lt;label for=\"text-input\" class=\"block text-sm font-medium text-slate-700 mb-2\">2. Add Text&lt;\/label>\n                    &lt;input type=\"text\" id=\"text-input\" placeholder=\"Enter text to add\" class=\"block w-full px-3 py-2 bg-white border border-slate-300 rounded-md text-sm shadow-sm placeholder-slate-400 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500\">\n                    &lt;button id=\"add-text-btn\" class=\"mt-2 w-full bg-blue-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors\">Add Text&lt;\/button>\n                &lt;\/div>\n                \n                &lt;!-- Save\/Reset -->\n                &lt;div>\n                    &lt;label class=\"block text-sm font-medium text-slate-700 mb-2\">3. Save or Reset&lt;\/label>\n                    &lt;button id=\"download-btn\" class=\"w-full bg-green-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-green-700 transition-colors\">Download New SVG File&lt;\/button>\n                    &lt;button id=\"reset-btn\" class=\"mt-2 w-full bg-red-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-red-700 transition-colors\">Reset and Clear&lt;\/button>\n                &lt;\/div>\n            &lt;\/aside>\n<\/pre>\n\n\n\n<p>HTML \u0e2a\u0e48\u0e27\u0e19\u0e41\u0e2a\u0e14\u0e07\u0e1c\u0e25 &#8211; \u0e40\u0e1b\u0e47\u0e19 div svg-container \u0e18\u0e23\u0e23\u0e21\u0e14\u0e32\u0e40\u0e17\u0e48\u0e32\u0e19\u0e31\u0e49\u0e19<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">            &lt;!-- SVG Display Column -->\n            &lt;main class=\"lg:col-span-2 bg-white p-2 rounded-lg shadow-lg\">\n                &lt;div id=\"svg-container\" class=\"w-full h-[600px]\">\n                    &lt;span id=\"svg-placeholder\" class=\"text-slate-500 text-center p-4\">Upload an SVG file to begin editing.&lt;\/span>\n                &lt;\/div>\n            &lt;\/main><\/pre>\n\n\n\n<p>\u0e40\u0e1e\u0e34\u0e48\u0e21 Javascript \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a control button<br>1. loadState &#8211; \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e42\u0e2b\u0e25\u0e14\u0e20\u0e32\u0e1e\u0e17\u0e35\u0e48\u0e40\u0e1b\u0e34\u0e14\u0e17\u0e34\u0e49\u0e07\u0e40\u0e2d\u0e32\u0e44\u0e27\u0e49\u0e25\u0e48\u0e32\u0e2a\u0e38\u0e14\u0e02\u0e36\u0e49\u0e19\u0e21\u0e32 + \u0e23\u0e27\u0e21\u0e16\u0e36\u0e07\u0e21\u0e35\u0e01\u0e32\u0e23\u0e41\u0e01\u0e49\u0e44\u0e02\u0e40\u0e2d\u0e32\u0e44\u0e27\u0e49\u0e14\u0e49\u0e27\u0e22<br>2. saveState &#8211; \u0e40\u0e01\u0e47\u0e1a\u0e20\u0e32\u0e1e\u0e17\u0e35\u0e48\u0e41\u0e01\u0e49\u0e44\u0e02\u0e25\u0e48\u0e32\u0e2a\u0e38\u0e14\u0e40\u0e2d\u0e32\u0e44\u0e27\u0e49\u0e40\u0e2d\u0e32\u0e44\u0e27\u0e49\u0e40\u0e1c\u0e37\u0e48\u0e2d\u0e15\u0e2d\u0e19\u0e40\u0e1b\u0e34\u0e14\u0e04\u0e23\u0e31\u0e49\u0e07\u0e2b\u0e19\u0e49\u0e32<br>3. actionListener &#8211; reset button : click &#8211; \u0e25\u0e1a\u0e20\u0e32\u0e1e\u0e2d\u0e2d\u0e01\u0e08\u0e32\u0e01\u0e2b\u0e19\u0e49\u0e32\u0e08\u0e2d<br>4. displaySvg<br>5. actionListener &#8211; file input : change<br>6. initializeDraggable &#8211; \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a add mouse listener (startDrag) \u0e43\u0e2b\u0e49\u0e01\u0e31\u0e1a svg element \u0e17\u0e35\u0e48\u0e21\u0e35 class draggable \u0e17\u0e38\u0e01\u0e15\u0e31\u0e27<br>7. getMousePosition &#8211; \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e2b\u0e32\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e17\u0e35\u0e48 mouse \u0e2d\u0e22\u0e39\u0e48<br>8. startDrag &#8211; \u0e22\u0e49\u0e32\u0e22\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e15\u0e32\u0e21 Mouse + add mouse listener \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a (drag + endDrag)<br>9. drag  &#8211; \u0e22\u0e49\u0e32\u0e22\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e15\u0e32\u0e21 Mouse<br>10. endDrag &#8211; end drag \u0e41\u0e25\u0e49\u0e27\u0e01\u0e47\u0e25\u0e1a mouse listener \u0e2d\u0e2d\u0e01 \u0e08\u0e30\u0e44\u0e14\u0e49\u0e44\u0e21\u0e48\u0e0b\u0e49\u0e33\u0e0b\u0e49\u0e2d\u0e19 \u0e40\u0e27\u0e25\u0e32\u0e40\u0e23\u0e34\u0e48\u0e21 move \u0e43\u0e2b\u0e21\u0e48<br>11. actionListener &#8211; add text button : click<br>12. actionListener &#8211; download button : click<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>\u0e44\u0e1f\u0e25\u0e4c\u0e40\u0e15\u0e47\u0e21<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!DOCTYPE html>\n&lt;html lang=\"en\">\n&lt;head>\n    &lt;meta charset=\"UTF-8\">\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    &lt;title>Interactive SVG Editor&lt;\/title>\n    &lt;script src=\"https:\/\/cdn.tailwindcss.com\">&lt;\/script>\n    &lt;link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n    &lt;link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n    &lt;link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;500;700&amp;display=swap\" rel=\"stylesheet\">\n    &lt;style>\n        body {\n            font-family: 'Inter', sans-serif;\n        }\n        \/* Style for the SVG container *\/\n        #svg-container {\n            width: 100%;\n            height: 100%;\n            border: 2px dashed #cbd5e1;\n            border-radius: 0.5rem;\n            background-color: #f8fafc;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            overflow: hidden; \/* Important for SVG scaling *\/\n        }\n        #svg-container.has-content {\n            border-style: solid;\n            border-color: #94a3b8;\n        }\n        \/* Add a grabbing cursor when an element is being dragged *\/\n        #svg-container.dragging {\n             cursor: grabbing;\n        }\n        \/* Add a move cursor to elements with the 'draggable' class *\/\n        #svg-container .draggable {\n            cursor: grab;\n            user-select: none;\n        }\n    &lt;\/style>\n&lt;\/head>\n&lt;body class=\"bg-slate-100 text-slate-800\">\n\n    &lt;div class=\"container mx-auto p-4 md:p-8\">\n        &lt;header class=\"text-center mb-8\">\n            &lt;h1 class=\"text-4xl font-bold text-slate-900\">Interactive SVG Editor&lt;\/h1>\n            &lt;p class=\"mt-2 text-lg text-slate-600\">Upload, drag, add text, and save your SVG. Your work is saved in the browser automatically.&lt;\/p>\n        &lt;\/header>\n\n        &lt;div class=\"grid grid-cols-1 lg:grid-cols-3 gap-8\">\n            &lt;!-- Controls Column -->\n            &lt;aside class=\"lg:col-span-1 bg-white p-6 rounded-lg shadow-lg h-fit\">\n                &lt;h2 class=\"text-2xl font-bold mb-4 border-b pb-2\">Controls&lt;\/h2>\n                \n                &lt;!-- File Upload -->\n                &lt;div class=\"mb-6\">\n                    &lt;label for=\"svg-upload\" class=\"block text-sm font-medium text-slate-700 mb-2\">1. Load SVG File&lt;\/label>\n                    &lt;input type=\"file\" id=\"svg-upload\" accept=\".svg,image\/svg+xml\" class=\"block w-full text-sm text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100\"\/>\n                    &lt;p class=\"mt-1 text-xs text-slate-500\">Elements with class=\"draggable\" will be movable.&lt;\/p>\n                &lt;\/div>\n\n                &lt;!-- Add Text -->\n                &lt;div class=\"mb-6\">\n                    &lt;label for=\"text-input\" class=\"block text-sm font-medium text-slate-700 mb-2\">2. Add Text&lt;\/label>\n                    &lt;input type=\"text\" id=\"text-input\" placeholder=\"Enter text to add\" class=\"block w-full px-3 py-2 bg-white border border-slate-300 rounded-md text-sm shadow-sm placeholder-slate-400 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500\">\n                    &lt;button id=\"add-text-btn\" class=\"mt-2 w-full bg-blue-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors\">Add Text&lt;\/button>\n                &lt;\/div>\n                \n                &lt;!-- Save\/Reset -->\n                &lt;div>\n                    &lt;label class=\"block text-sm font-medium text-slate-700 mb-2\">3. Save or Reset&lt;\/label>\n                    &lt;button id=\"download-btn\" class=\"w-full bg-green-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-green-700 transition-colors\">Download New SVG File&lt;\/button>\n                    &lt;button id=\"reset-btn\" class=\"mt-2 w-full bg-red-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-red-700 transition-colors\">Reset and Clear&lt;\/button>\n                &lt;\/div>\n            &lt;\/aside>\n\n            &lt;!-- SVG Display Column -->\n            &lt;main class=\"lg:col-span-2 bg-white p-2 rounded-lg shadow-lg\">\n                &lt;div id=\"svg-container\" class=\"w-full h-[600px]\">\n                    &lt;span id=\"svg-placeholder\" class=\"text-slate-500 text-center p-4\">Upload an SVG file to begin editing.&lt;\/span>\n                &lt;\/div>\n            &lt;\/main>\n        &lt;\/div>\n    &lt;\/div>\n\n    &lt;script>\n        window.onload = () => {\n            \/\/ DOM Elements\n            const svgContainer = document.getElementById('svg-container');\n            const svgPlaceholder = document.getElementById('svg-placeholder');\n            const fileInput = document.getElementById('svg-upload');\n            const textInput = document.getElementById('text-input');\n            const addTextBtn = document.getElementById('add-text-btn');\n            const downloadBtn = document.getElementById('download-btn');\n            const resetBtn = document.getElementById('reset-btn');\n\n            \/\/ SVG Manipulation State\n            const SVG_NAMESPACE = \"http:\/\/www.w3.org\/2000\/svg\";\n            let activeElement = null;\n            let offset = { x: 0, y: 0 };\n            let currentSVG = null;\n\n            \/\/ --- LOCAL STORAGE &amp; INITIALIZATION ---\n\n            \/**\n             * Loads SVG content from localStorage if it exists.\n             *\/\n            function loadState() {\n                const savedSVG = localStorage.getItem('savedUserSVG');\n                if (savedSVG) {\n                    displaySVG(savedSVG);\n                }\n            }\n\n            \/**\n             * Saves the current SVG content to localStorage.\n             *\/\n            function saveState() {\n                if (currentSVG) {\n                    const svgString = new XMLSerializer().serializeToString(currentSVG);\n                    localStorage.setItem('savedUserSVG', svgString);\n                    console.log(\"SVG state saved.\");\n                }\n            }\n\n            \/**\n             * Resets the application state and clears localStorage.\n             *\/\n            resetBtn.addEventListener('click', () => {\n                if (confirm(\"Are you sure you want to clear your current work? This cannot be undone.\")) {\n                    localStorage.removeItem('savedUserSVG');\n                    svgContainer.innerHTML = '';\n                    svgContainer.appendChild(svgPlaceholder);\n                    svgContainer.classList.remove('has-content');\n                    currentSVG = null;\n                }\n            });\n\n\n            \/\/ --- SVG DISPLAY &amp; FILE HANDLING ---\n\n            \/**\n             * Injects SVG string into the container and sets up interactivity.\n             * @param {string} svgString - The SVG content as a string.\n             *\/\n            function displaySVG(svgString) {\n                svgContainer.innerHTML = svgString;\n                currentSVG = svgContainer.querySelector('svg');\n                if (currentSVG) {\n                    svgContainer.classList.add('has-content');\n                    \/\/ Ensure the SVG scales within the container\n                    currentSVG.setAttribute('width', '100%');\n                    currentSVG.setAttribute('height', '100%');\n                    initializeDraggable(currentSVG);\n                } else {\n                     svgContainer.classList.remove('has-content');\n                }\n            }\n\n            \/**\n             * Handles the file upload event.\n             *\/\n            fileInput.addEventListener('change', (event) => {\n                const file = event.target.files[0];\n                if (file &amp;&amp; file.type === \"image\/svg+xml\") {\n                    const reader = new FileReader();\n                    reader.onload = (e) => {\n                        displaySVG(e.target.result);\n                        saveState(); \/\/ Save the newly loaded SVG as the current state\n                    };\n                    reader.readAsText(file);\n                } else {\n                    alert(\"Please upload a valid .svg file.\");\n                }\n            });\n\n\n            \/\/ --- DRAG AND DROP LOGIC ---\n\n            \/**\n             * Sets up mousedown listeners on all 'draggable' elements within the SVG.\n             * @param {SVGElement} svgElement - The root SVG element.\n             *\/\n            function initializeDraggable(svgElement) {\n                const draggables = svgElement.querySelectorAll('.draggable');\n                draggables.forEach(el => {\n                    el.addEventListener('mousedown', startDrag);\n                });\n            }\n\n            \/**\n             * Converts screen coordinates to SVG coordinates.\n             * @param {MouseEvent} event - The mouse event.\n             * @returns {{x: number, y: number}} The coordinates within the SVG space.\n             *\/\n            function getMousePosition(event) {\n                const CTM = currentSVG.getScreenCTM();\n                return {\n                    x: (event.clientX - CTM.e) \/ CTM.a,\n                    y: (event.clientY - CTM.f) \/ CTM.d\n                };\n            }\n\n            \/**\n             * Initiates the drag operation.\n             * @param {MouseEvent} event - The mousedown event.\n             *\/\n            function startDrag(event) {\n                event.preventDefault();\n                if (event.target.classList.contains('draggable')) {\n                    activeElement = event.target;\n                    svgContainer.classList.add('dragging');\n                    offset = getMousePosition(event);\n\n                    \/\/ Handle existing transforms\n                    const transform = activeElement.transform.baseVal;\n                    if (transform.length === 0 || transform.getItem(0).type !== SVGTransform.SVG_TRANSFORM_TRANSLATE) {\n                        const translate = currentSVG.createSVGTransform();\n                        translate.setTranslate(0, 0);\n                        activeElement.transform.baseVal.insertItemBefore(translate, 0);\n                    }\n                    const initialTransform = transform.getItem(0).matrix;\n                    offset.x -= initialTransform.e;\n                    offset.y -= initialTransform.f;\n\n                    \/\/ Add listeners to the whole container for smoother dragging\n                    svgContainer.addEventListener('mousemove', drag);\n                    svgContainer.addEventListener('mouseup', endDrag);\n                    svgContainer.addEventListener('mouseleave', endDrag);\n                }\n            }\n\n            \/**\n             * Performs the drag translation.\n             * @param {MouseEvent} event - The mousemove event.\n             *\/\n            function drag(event) {\n                if (activeElement) {\n                    event.preventDefault();\n                    const coord = getMousePosition(event);\n                    const transform = activeElement.transform.baseVal.getItem(0);\n                    transform.setTranslate(coord.x - offset.x, coord.y - offset.y);\n                }\n            }\n\n            \/**\n             * Ends the drag operation and saves the state.\n             *\/\n            function endDrag() {\n                if (activeElement) {\n                    activeElement = null;\n                    svgContainer.classList.remove('dragging');\n                    svgContainer.removeEventListener('mousemove', drag);\n                    svgContainer.removeEventListener('mouseup', endDrag);\n                    svgContainer.removeEventListener('mouseleave', endDrag);\n                    saveState(); \/\/ Save state after modification\n                }\n            }\n\n            \/\/ --- ADD TEXT LOGIC ---\n            \n            addTextBtn.addEventListener('click', () => {\n                const textContent = textInput.value;\n                if (!textContent || !currentSVG) {\n                    alert(\"Please load an SVG and enter some text first.\");\n                    return;\n                }\n\n                const newText = document.createElementNS(SVG_NAMESPACE, \"text\");\n                const viewBox = currentSVG.viewBox.baseVal;\n                const randomX = viewBox.x + viewBox.width \/ 2;\n                const randomY = viewBox.y + viewBox.height \/ 2;\n\n                newText.setAttribute(\"x\", randomX);\n                newText.setAttribute(\"y\", randomY);\n                newText.setAttribute(\"font-size\", \"20\");\n                newText.setAttribute(\"font-family\", \"Inter, sans-serif\");\n                newText.setAttribute(\"fill\", \"#1e293b\");\n                newText.setAttribute(\"text-anchor\", \"middle\");\n                newText.classList.add('draggable'); \/\/ Make new text draggable by default\n                newText.textContent = textContent;\n\n                currentSVG.appendChild(newText);\n                initializeDraggable(currentSVG); \/\/ Re-initialize to include the new text element\n                saveState(); \/\/ Save state after adding text\n                textInput.value = ''; \/\/ Clear input field\n            });\n\n\n            \/\/ --- DOWNLOAD LOGIC ---\n\n            downloadBtn.addEventListener('click', () => {\n                if (!currentSVG) {\n                    alert(\"There is no SVG to download. Please load one first.\");\n                    return;\n                }\n                const svgData = new XMLSerializer().serializeToString(currentSVG);\n                const blob = new Blob([svgData], { type: \"image\/svg+xml;charset=utf-8\" });\n                const url = URL.createObjectURL(blob);\n                const link = document.createElement(\"a\");\n                link.href = url;\n                link.download = \"modified-svg-image.svg\";\n                document.body.appendChild(link);\n                link.click();\n                document.body.removeChild(link);\n                URL.revokeObjectURL(url);\n            });\n\n            \/\/ --- INITIAL LOAD ---\n            loadState();\n        };\n    &lt;\/script>\n&lt;\/body>\n&lt;\/html>\n<\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23 \u0e2a\u0e23\u0e49\u0e32\u0e07 \u0e2b\u0e23\u0e37\u0e2d \u0e41\u0e01\u0e49\u0e44\u0e02 \u0e20\u0e32\u0e1e SVG \u0e42\u0e14\u0e22\u0e40\u0e23\u0e32\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16\u0e17\u0e33\u0e01\u0e32\u0e23 add text \u0e41\u0e25\u0e30\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16 \u0e04\u0e25\u0e34\u0e01\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e40\u0e25\u0e37\u0e48\u0e2d\u0e19 Object \u0e17\u0e35\u0e48\u0e40\u0e23\u0e32\u0e40\u0e2b\u0e47\u0e19\u0e43\u0e19\u0e44\u0e1f\u0e25\u0e4c\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e\u0e19\u0e32\u0e21\u0e2a\u0e01\u0e38\u0e25 SVG \u0e44\u0e1b\u0e22\u0e31\u0e07\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e15\u0e48\u0e32\u0e07\u0e46\u0e44\u0e14\u0e49&hellip;<\/p>\n","protected":false},"author":1,"featured_media":166,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-313","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-programming"],"_links":{"self":[{"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/posts\/313","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/comments?post=313"}],"version-history":[{"count":6,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/posts\/313\/revisions"}],"predecessor-version":[{"id":324,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/posts\/313\/revisions\/324"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/media\/166"}],"wp:attachment":[{"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/media?parent=313"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/categories?post=313"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/tags?post=313"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}