इस लेख के मुख्य बिंदु

  • ChatGPT के साथ दो-तरफ़ा काम करते हुए मैंने SVG संपादक बनाने की कोशिश की, लेकिन कई बार ठोकर लगी। यहाँ उन्हीं कठिन मोड़ों का सार है।
  • अकेले मैं यह कभी पूरा नहीं कर पाता। जेनरेटिव एआई की कोडिंग क्षमता वाकई असरदार है।
  • लेकिन सब कुछ AI पर छोड़ देने से भी काम नहीं बना। पूरे प्रोजेक्ट का समन्वय, गलतफहमी की सुधार और समस्या-समाधान की दिशा दिखाने जैसे प्लेइंग मैनेजर की जिम्मेदारियाँ मुझे ही निभानी पड़ीं।
  • इसलिए यह अनुभव भी एआई युग में कैसे टिके रहें, इसकी सीख देता है।

सिर्फ ब्राउज़र में चलने वाले SVG संपादक का कार्यान्वयन और विनिर्देश व्याख्या

SVG क्या है

SVG (Scalable Vector Graphics) W3C द्वारा मानकीकृत एक XML-आधारित वेक्टर इमेज फ़ॉर्मेट है। रास्टर इमेज (PNG या JPEG आदि) से अलग, यह बड़ा या छोटा करने पर भी गुणवत्ता नहीं खोता। इसके अलावा इसे DOM के हिस्से के रूप में सीधे HTML दस्तावेज़ में एम्बेड किया जा सकता है, इसलिए JavaScript और CSS की मदद से गतिशील संचालन और स्टाइल परिवर्तन संभव हैं। विनिर्देश W3C SVG स्पेसिफिकेशन का पालन करते हैं और आकृतियाँ, पाठ, पथ, ग्रेडिएंट, फ़िल्टर जैसी अभिव्यक्तियों को शामिल करते हैं।

SVG निम्नलिखित उपयोग मामलों में व्यापक है:

  • वेब पेज पर आइकन और चार्ट दिखाना
  • डायग्राम या चार्ट का गतिशील निर्माण
  • उपयोगकर्ता इंटरैक्शन सहित ड्राइंग एप्लिकेशन
  • प्रिंट तथा UI डिज़ाइन के लिए एसेट तैयार करना

इस SVG संपादक का अवलोकन

यह संपादक केवल ब्राउज़र में चलने वाला पूर्ण क्लाइंट-साइड टूल है। उपयोगकर्ता आयत और अंडाकार जैसी बुनियादी आकृतियों के साथ-साथ सितारा, संवाद बुलबुला, बादल, दिल और फ्री-हैंड कर्व बनाने वाले पेन मोड तक का उपयोग कर सकते हैं। तैयार आकृतियों को स्थानांतरित, स्केल और घुमाया जा सकता है, साथ ही अलाइनमेंट, स्नैप, समूहकरण और लेयर सूची भी उपलब्ध है। अंतिम परिणाम को SVG या PNG फ़ॉर्मेट में निर्यात किया जा सकता है।

आगे की धाराओं में हम देखेंगे कि SVG की विशिष्टताओं को JavaScript में कैसे लागू किया गया और कहाँ अड़चनें आईं।

कार्यान्वयन में विनिर्देश का प्रतिबिंब और कठिन बिंदु

आकृतियों का जोड़ना और गुणों का प्रबंधन

SVG विनिर्देश प्रत्येक आकृति के लिए अपने तत्व और गुण परिभाषित करता है। आयत के लिए <rect>, अंडाकार के लिए <ellipse>, बहुभुज के लिए <polygon> का प्रयोग होता है, और उनके x, y, width, height या cx, cy, rx, ry, points जैसे गुणों को सही तरह सेट करना पड़ता है। सितारा या दिल जैसी जटिल आकृतियाँ <path> से बेज़ियर कर्व और आर्क कमांड मिलाकर बनाई गईं।

कार्यान्वयन उदाहरण (आयत जोड़ना)

function addRectangle(x, y, width, height) {
  const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  rect.setAttribute("x", x);
  rect.setAttribute("y", y);
  rect.setAttribute("width", width);
  rect.setAttribute("height", height);
  rect.setAttribute("fill", "transparent");
  rect.setAttribute("stroke", "#000");
  svg.appendChild(rect);
}

आयत और अंडाकार सरल हैं, लेकिन बहुभुज या कस्टम शेप के लिए निर्देशांक की गणना करनी पड़ती है। उदाहरण के तौर पर सितारा बनाने के लिए केंद्र कोण को बराबर हिस्सों में बाँटकर शिखर निकालते हैं और points गुण में जोड़ते हैं।

function createStar(cx, cy, spikes, outerR, innerR) {
  let points = "";
  const step = Math.PI / spikes;
  for (let i = 0; i < 2 * spikes; i++) {
    const r = i % 2 === 0 ? outerR : innerR;
    const x = cx + r * Math.cos(i * step);
    const y = cy + r * Math.sin(i * step);
    points += `${x},${y} `;
  }
  const polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
  polygon.setAttribute("points", points.trim());
  polygon.setAttribute("stroke", "#000");
  polygon.setAttribute("fill", "transparent");
  svg.appendChild(polygon);
}

रूपांतरण और निर्देशांक प्रणाली का प्रबंधन

SVG में transform गुण के माध्यम से translate, rotate, scale आदि संयोजित करके आकृतियों को नियंत्रित किया जा सकता है। चुनौती यह थी कि चयनित आकृति का बाउंडिंग बॉक्स लेते समय स्थानीय और वैश्विक निर्देशांक के फर्क को समेटना पड़े। इसके लिए getScreenCTM() का उपयोग कर ग्लोबल निर्देशांक में परिवर्तित करके आयत की गणना की।

कार्यान्वयन उदाहरण (निर्देशांक परिवर्तन लागू करना)

function getTransformedBBox(element) {
  const bbox = element.getBBox();
  const matrix = element.getScreenCTM();
  const points = [
    svg.createSVGPoint(), svg.createSVGPoint(),
    svg.createSVGPoint(), svg.createSVGPoint()
  ];
  points[0].x = bbox.x; points[0].y = bbox.y;
  points[1].x = bbox.x + bbox.width; points[1].y = bbox.y;
  points[2].x = bbox.x; points[2].y = bbox.y + bbox.height;
  points[3].x = bbox.x + bbox.width; points[3].y = bbox.y + bbox.height;
  return points.map(p => p.matrixTransform(matrix));
}

इससे घुमाव या स्केलिंग के बाद भी सही चयन फ्रेम बनाना संभव हुआ।

स्नैप फीचर और ग्रिड

ग्रिड को SVG के <pattern> तत्व से पृष्ठभूमि में खींचा गया। स्नैपिंग प्रक्रिया में JavaScript से निर्देशांक की गणना कर उन्हें तय अंतराल पर गोल किया गया।

function snapToGrid(value, gridSize) {
  return Math.round(value / gridSize) * gridSize;
}

ऑब्जेक्ट स्नैप के लिए अन्य आकृतियों के केंद्र या किनारों को स्कैन कर सबसे नज़दीकी बिंदु का पता लगाया और सहायक रेखाएँ दिखाकर आकर्षित कराया। इससे UI की सटीकता काफी बढ़ गई।

टेक्स्ट संपादन

SVG के <text> तत्व को सीधे संपादित नहीं किया जा सकता, इसलिए इनलाइन संपादन हेतु HTML <input> को अस्थायी रूप से ओवरले किया गया।

function editTextElement(textElem) {
  const input = document.createElement("input");
  input.type = "text";
  input.value = textElem.textContent;
  document.body.appendChild(input);
  const bbox = textElem.getBoundingClientRect();
  input.style.position = "absolute";
  input.style.left = bbox.x + "px";
  input.style.top = bbox.y + "px";
  input.focus();
  input.addEventListener("blur", () => {
    textElem.textContent = input.value;
    document.body.removeChild(input);
  });
}

पाथ और मुक्त वक्र

SVG के <path> में M, L, C, A जैसे कमांड जोड़कर ड्रॉ किया जाता है। हमने पहले सीधी रेखा आधारित संपादन से शुरुआत की, जहाँ हर क्लिक पर निर्देशांक जोड़े जाते हैं।

let pathData = "M100,100";
function addPathPoint(x, y) {
  pathData += ` L${x},${y}`;
  path.setAttribute("d", pathData);
}

आउटपुट (SVG/PNG निर्यात)

सहेजते समय सहायक तत्वों को हटाकर एक्सपोर्ट किया जाता है। PNG परिवर्तन Canvas के माध्यम से किया गया।

function exportToPNG(svgElement) {
  const serializer = new XMLSerializer();
  const source = serializer.serializeToString(svgElement);
  const img = new Image();
  const svgBlob = new Blob([source], { type: "image/svg+xml;charset=utf-8" });
  const url = URL.createObjectURL(svgBlob);
  img.onload = () => {
    const canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;
    const ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    canvas.toBlob(blob => {
      const a = document.createElement("a");
      a.href = URL.createObjectURL(blob);
      a.download = "drawing.png";
      a.click();
    });
  };
  img.src = url;
}

कैनवस स्क्रॉल समर्थन

बड़े SVG को संभालने के लिए <div style="overflow:auto"> में लपेटकर आवश्यकतानुसार स्क्रॉल बार दिखाए गए।

निष्कर्ष

SVG की विनिर्देश शक्ति प्रबल है, लेकिन कार्यान्वयन में निर्देशांक परिवर्तन और पाथ परिभाषा जैसी अड़चनें आती हैं। इस संपादक में हमने इन्हें व्यवस्थित करते हुए निम्नलिखित हासिल किए:

  • आकृति तत्वों के गुणों का संचालन (rect, ellipse, polygon, path)
  • transform और निर्देशांक प्रणाली का सही प्रबंधन
  • ग्रिड और स्नैप का कार्यान्वयन
  • इनलाइन टेक्स्ट संपादन
  • पाथ संपादन (सीधी रेखा आधारित)
  • PNG/SVG दोनों फॉर्मेट में निर्यात
  • स्क्रॉल योग्य बड़ा कैनवस समर्थन

SVG की अभिव्यक्ति क्षमता को पूरा उपयोग में लाकर हम केवल ब्राउज़र में चलने वाला उच्च-कार्यशीलता संपादक बना पाए।