PerfectVector
โดย Irene Kim

SVG.js: นำเข้างาน แก้พาธ และส่งออก SVG

นำเข้ามาร์กอัป SVG ที่เชื่อถือได้ใน SVG.js เปลี่ยนสีพาธที่มีชื่อ แล้วตรวจการส่งออกรูททั้งหมดเทียบกับเฉพาะองค์ประกอบลูก รักษารูปทรงและตรวจไฟล์ที่บันทึก

อ่าน 5 นาที
ในหน้านี้

หากต้องการนำเข้างานใน SVG.js เพื่อแก้ไขเฉพาะส่วน ให้ส่งมาร์กอัป SVG ที่เชื่อถือได้ไปยัง .svg() หาองค์ประกอบที่ต้องการด้วย ID ที่ตั้งไว้ แล้วเปลี่ยนสีเติมขององค์ประกอบนั้น ส่งออกรูท SVG ทั้งหมดของงานเพื่อให้ได้ไฟล์ที่ใช้งานได้เดี่ยว ๆ การส่งออกเฉพาะองค์ประกอบลูกเป็นเพียงส่วนย่อย ซึ่งต้องมีรูทพร้อมขนาดและ viewBox ที่ต้องการก่อนส่งมอบเป็นเอกสาร

คู่มือนี้ใช้รูปสี่เหลี่ยมข้าวหลามตัดกลวงต้นฉบับกับสามเหลี่ยมแยกกัน สามเหลี่ยมเปลี่ยนจากสีเหลืองดินเป็นสีคอรัล ส่วนรูปข้าวหลามตัดยังคงสีเขียวอมฟ้าและช่องว่างเดิม เราทดสอบตัวอย่างครบชุดด้านล่างด้วย SVG.js 3.2.6 โดยตรึงตามรุ่น 3.2.6 อย่างเป็นทางการ ข้อสังเกตเหล่านี้เกี่ยวกับพาธเรียบง่ายสองเส้นที่สร้างขึ้น ไม่ได้ครอบคลุมทุกความสามารถที่ SVG อื่นอาจมี

ตัดสินใจว่าต้องการรูปทรงที่แก้ไขได้หรือภาพ

คำสั่ง import ของ JavaScript ที่ให้ URL ของแอสเซ็ตตอบโจทย์เรื่องการโหลด แต่ไม่ได้ใส่พาธจากไฟล์ลงในต้นไม้ SVG ที่ใช้งานอยู่ของโปรแกรมแก้ไขโดยอัตโนมัติ เอกสารการนำเข้าและส่งออกปัจจุบัน ของ SVG.js อธิบายการนำเข้ามาร์กอัปและการรับองค์ประกอบ SVG ชั้นนอกหรือเฉพาะองค์ประกอบลูก

รูปแบบข้อมูลสิ่งที่ตัวอย่างของเราเข้าถึงได้สิ่งที่ตรวจในการส่งมอบ
มาร์กอัปที่เชื่อถือได้นำเข้าในรูท SVG ที่ทำงานอยู่พาธชื่อ accentตรวจรูทที่แก้ไขแล้วและเปิดไฟล์ส่งออกอีกครั้ง
SVG ที่โหลดผ่านองค์ประกอบภาพองค์ประกอบภาพทั้งชิ้นตรวจเนื้อหาภาพแยกต่างหาก
มาร์กอัปเฉพาะองค์ประกอบลูกองค์ประกอบพาธสองตัวที่ไม่มีรูทเดิมกำหนดรูท ขนาด และ viewBox ให้ชัดเจน

SVG ภายในภาพยังคงเป็นเนื้อหา SVG แต่ในชุดควบคุมที่ห่อภาพของเรา ต้นไม้ SVG ภายนอกมีองค์ประกอบภาพหนึ่งตัวและไม่มีพาธโดยตรง การค้นหา accent ในต้นไม้นั้นจึงไม่พบอะไร และไม่ได้เข้าไปค้นในเอกสารของภาพที่ฝังอยู่ MDN อธิบายบริบท SVG ที่ใช้เป็นภาพ ซึ่งแยกต่างหากและมีข้อจำกัด อย่าสรุปโมเดลการแก้ไขจากภาพที่เรนเดอร์คมชัด

สำหรับการตัดสินใจเรื่องการฝังโดยรวม อ่าน SVG แบบ inline เทียบกับภาพ หากต้องการโมเดลอ็อบเจ็กต์ canvas ของ Fabric แทน คู่มือการนำเข้าด้วย Fabric.js จะอธิบายตัวแยกวิเคราะห์และ API แก้ไขอีกแบบ

แก้พาธที่มีชื่อหนึ่งเส้นและบันทึกทุกรูปแบบ

บันทึกโค้ดทั้งหมดด้านล่างเป็น starter.html ในโฟลเดอร์ว่าง ให้บริการผ่าน HTTP เช่นใช้ python3 -m http.server 8000 แล้วเปิด http://localhost:8000/starter.html สคริปต์โหลดแพ็กเกจรุ่นที่ตรึงไว้จาก CDN จึงต้องมีการเชื่อมต่อเครือข่าย

หน้าเว็บแสดงงานต้นฉบับ สำเนาที่แก้ไขได้ขณะทำงาน และชุดควบคุมที่ห่อภาพ ลิงก์ดาวน์โหลดไฟล์หลักต้นฉบับ รูทเต็มก่อนและหลังแก้ไข ส่วนข้อความเฉพาะองค์ประกอบลูก สำเนาที่ใส่รูทใหม่ และตัวห่อภาพ รายงานอ่านพิกเซลจากภาพ SVG ที่วาดลง canvas ไม่ได้ส่งออก PNG

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Original SVG.js artwork import and export</title>
<style>
  body { margin: 24px; font: 16px system-ui; background: #f6f2e8; }
  main { display: grid; grid-template-columns: repeat(3, 260px); gap: 20px; }
  figure { margin: 0; }
  svg, img { display: block; background: white; border: 1px solid #bbb; }
  pre { white-space: pre-wrap; font-size: 12px; }
</style>
<h1>Trusted artwork, one fill edit and two export forms</h1>
<main>
  <figure><img id="original" alt="Original artwork"><figcaption>Original SVG image</figcaption></figure>
  <figure><div id="live"></div><figcaption>Imported live SVG, accent edited</figcaption></figure>
  <figure><div id="wrapper"></div><figcaption>Image wrapper, original artwork</figcaption></figure>
</main>
<p id="downloads"></p>
<pre id="report"></pre>
<script src="https://cdn.jsdelivr.net/npm/@svgdotjs/svg.js@3.2.6/dist/svg.min.js"></script>
<script>
  const markup = '<svg xmlns="http://www.w3.org/2000/svg" id="original-artwork" width="240" height="160" viewBox="0 0 240 160">'
    + '<path id="body" d="M80 20 L140 80 L80 140 L20 80 Z M80 56 L104 80 L80 104 L56 80 Z" fill="#176b5b" fill-rule="evenodd"/>'
    + '<path id="accent" d="M172 48 L220 80 L172 112 Z" fill="#d4a53a"/></svg>';
  const url = text => 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(text);
  async function pixels(text) {
    const image = new Image();
    image.src = url(text);
    await image.decode();
    const canvas = document.createElement('canvas');
    canvas.width = 240;
    canvas.height = 160;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0);
    const rgba = (x, y) => [...ctx.getImageData(x, y, 1, 1).data];
    return { naturalWidth: image.naturalWidth, naturalHeight: image.naturalHeight,
      opening: rgba(80, 80), body: rgba(80, 40), accent: rgba(188, 80), corner: rgba(0, 0) };
  }
  function inventory(text) {
    const node = new DOMParser().parseFromString(text, 'image/svg+xml').documentElement;
    return { root: node.localName, viewBox: node.getAttribute('viewBox'),
      paths: [...node.querySelectorAll('path')].map(p => ({ id: p.id, d: p.getAttribute('d'), fill: p.getAttribute('fill') })),
      imageCount: node.querySelectorAll('image').length };
  }
  async function run() {
    document.getElementById('original').src = url(markup);
    const host = SVG().addTo('#live').size(240, 160).viewbox(0, 0, 240, 160);
    host.svg(markup);
    const artwork = host.findOne('#original-artwork');
    const before = artwork.svg();
    const accent = artwork.findOne('#accent');
    const beforeD = accent.attr('d');
    accent.fill('#b95434');
    const full = artwork.svg();
    const children = artwork.svg(false);
    const rewrapped = SVG().size(240, 160).viewbox(0, 0, 240, 160);
    rewrapped.svg(children);
    const wrappedFull = rewrapped.svg();
    const imageWrapper = SVG().addTo('#wrapper').size(240, 160).viewbox(0, 0, 240, 160);
    imageWrapper.image(url(markup)).size(240, 160);
    const wrapperFull = imageWrapper.svg();
    const files = { 'master.svg': markup, 'before.svg': before, 'edited.svg': full,
      'children-only.txt': children, 'rewrapped.svg': wrappedFull, 'image-wrapper.svg': wrapperFull };
    for (const [name, text] of Object.entries(files)) {
      const a = document.createElement('a');
      a.href = name.endsWith('.txt') ? 'data:text/plain;charset=utf-8,' + encodeURIComponent(text) : url(text);
      a.download = name;
      a.textContent = name;
      a.style.marginRight = '12px';
      document.getElementById('downloads').append(a);
    }
    const results = {};
    for (const [name, text] of Object.entries(files)) {
      if (name.endsWith('.svg')) results[name] = { inventory: inventory(text), pixels: await pixels(text) };
    }
    document.getElementById('report').textContent = JSON.stringify({
      requestedPackageVersion: '3.2.6',
      scriptURL: document.querySelector('script[src]').src,
      selectedAccentFound: Boolean(accent), selectedAccentDUnchanged: beforeD === accent.attr('d'),
      allPathDUnchanged: JSON.stringify(inventory(before).paths.map(p => p.d)) === JSON.stringify(inventory(full).paths.map(p => p.d)),
      childrenHasOuterSVG: /<svg[\s>]/i.test(children),
      wrapperDirectPathCount: imageWrapper.find('path').length,
      wrapperAccentFound: Boolean(imageWrapper.findOne('#accent')), results
    }, null, 2);
  }
  run();
</script>
</html>
 

มาร์กอัปที่นำเข้ามีรูทซ้อนชื่อ original-artwork ตัวอย่างเลือกรูทนั้นก่อนส่งออก สำเนาส่งมอบจึงมี viewport 240 × 160 ตามที่สร้างไว้ แทนการอาศัยคอนเทนเนอร์แม่ที่ไม่เกี่ยวข้อง จากนั้นจึงเลือก accent ภายในรูท SVG.js มีเอกสารเรื่องการอ้างอิงด้วย selector และเมธอดขององค์ประกอบ การตั้งชื่อส่วนต่าง ๆ อย่างชัดเจนช่วยไม่ให้ต้องพึ่งลำดับพาธ

โค้ดนี้ตั้งใจจำกัดไว้กับมาร์กอัปคงที่ของตัวเอง และ ไม่ใช่เครื่องมือกรองความปลอดภัยสำหรับไฟล์ SVG ที่อัปโหลด การแยกวิเคราะห์หรือแทรกสตริงใด ๆ ไม่ได้ยืนยันว่าสตริงนั้นปลอดภัย เอกสาร DOMParser ของ MDN แยกการแยกวิเคราะห์ออกจากความเสี่ยงของการใส่เนื้อหาที่ไม่น่าเชื่อถือ ออกแบบนโยบายตรวจสอบและความเชื่อถือแยกต่างหากก่อนปรับใช้กับการอัปโหลด

ตรวจผลที่บันทึก ไม่ใช่เฉพาะตัวอย่างที่ทำงานอยู่

การรันจริงในเบราว์เซอร์ของเราได้ผลตรวจดังนี้:

รูปแบบที่บันทึกพาธในต้นไม้เอกสารภาพสีเติมสามเหลี่ยม
ไฟล์หลักต้นฉบับ20#d4a53a
รูทเต็มก่อนแก้ไข20#d4a53a
รูทเต็มหลังแก้ไข20#b95434
องค์ประกอบลูกที่ใส่ในรูทเต็มใหม่20#b95434
ตัวห่อภาพ01สีเหลืองดินเดิมภายในข้อมูลภาพ

สตริง d ของพาธต้นฉบับทั้งสองเหมือนเดิมในการส่งออกก่อนและหลังแก้ไข จุดตัวอย่างภายในสามเหลี่ยมเปลี่ยนจาก RGBA (212, 165, 58, 255) เป็น (185, 84, 52, 255) จุดตัวอย่างภายในรูปข้าวหลามตัดยังเป็น (23, 107, 91, 255) ช่องว่างและมุมด้านนอกยังมีอัลฟา 0 ไฟล์ SVG แบบเดี่ยวทั้งห้าเปิดซ้ำเป็นภาพได้ โดยมีขนาดตามธรรมชาติ 240 × 160

เปลี่ยนส่วนที่มีชื่อเพียงส่วนเดียว
ภาพเปรียบเทียบแนวคิดงานรูปข้าวหลามตัดกลวงที่เหมือนกัน โดยมีเพียงสามเหลี่ยมแยกที่เปลี่ยนจากสีทองเป็นคอรัล
ภาพแนวคิดของการแก้สีเฉพาะส่วน กรอบตกแต่งและลูกศรเป็นสัญลักษณ์ในแผนภาพ ส่วนตัวอย่างที่รันจริง รายการองค์ประกอบในซอร์สที่บันทึก และการตรวจพิกเซลในเบราว์เซอร์เป็นหลักฐานจริง

ผลเหล่านี้ยืนยันการแก้ไขที่ต้องการสำหรับชุดทดสอบนี้ ไม่ได้ยืนยันการรักษา XML ให้เหมือนเดิมทุกไบต์ การรองรับฟิลเตอร์หรือ CSS ทั้งหมด หรือความเหมาะสมกับกระบวนการผลิต การแปลงเป็นข้อความของไลบรารีอาจเปลี่ยนมาร์กอัปของเอกสาร แต่คงสตริงพาธเฉพาะเหล่านี้ไว้ เก็บไฟล์หลักต้นฉบับคู่กับสำเนาส่งมอบ

สำหรับงานของคุณ ให้ตรวจโครงสร้างที่ใช้จริง ได้แก่ชื่อและกลุ่ม การคลิป เกรเดียนต์ การแปลงรูป เส้นขอบ และข้อความ เปิดไฟล์ส่งออกในแอปที่รับงาน แล้วลองแก้ไขตามที่ต้องการซ้ำ คู่มือแก้ไข SVG ช่วยวิเคราะห์โครงสร้างต้นทางก่อนแก้โค้ดนำเข้าได้

มองการส่งออกเฉพาะองค์ประกอบลูกเป็นส่วนย่อยที่ตั้งใจใช้

ในการรันนี้ artwork.svg(false) คืนพาธสองเส้นโดยไม่มีแท็ก svg ชั้นนอก และไม่รวม viewport ของรูทงาน รูท SVG แยกชื่อ rewrapped กำหนดขนาด 240 × 160 และ viewBox เดิมก่อนรับองค์ประกอบลูก ไฟล์ที่ส่งออกจึงเรนเดอร์สามเหลี่ยมคอรัลตามที่ตรวจไว้

เลือกเอกสารครบชุดหรือส่วนย่อยตามปลายทาง สำหรับการดาวน์โหลดแบบเดี่ยว ให้เก็บรูทเต็มที่ต้องการ หากจะใส่ในงานวาดเดิม ให้ตัดสินใจว่าองค์ประกอบลูกจะใช้ระบบพิกัดแม่ สไตล์ที่สืบทอด และนิยามใด ชุดทดสอบเรียบง่ายนี้ไม่มีทรัพยากรภายนอกหรือนิยามร่วม จึงไม่ได้ทดสอบว่าส่วนย่อยที่มีการอ้างอิงนำสิ่งที่ต้องพึ่งพามาครบหรือไม่

การส่งออกตัวห่อภาพของเรายังคง SVG ที่เข้ารหัสไว้ ซึ่งเมื่อถอดรหัสแล้วมีไบต์ตรงกับไฟล์หลักต้นฉบับ ชุดควบคุมนี้แสดงความแตกต่างของรูปแบบข้อมูล ไม่ใช่การแปลงเป็นราสเตอร์ หากต้นทางมีรูปทรงเวกเตอร์ที่ใช้ได้อยู่แล้ว ให้แก้การเลือกองค์ประกอบหรือขอบเขตการส่งออกก่อนลากเส้นตามใหม่

เมื่อต้นทางไม่มีพาธให้แก้ไข

หากภาพ PNG หรือ JPG สีเรียบของคุณต้องการพื้นที่ที่เปลี่ยนสีแยกกันได้ ให้เตรียม SVG ตัวเลือกด้วย PerfectVector ตรวจแนวขอบและช่องว่าง แล้วตั้งชื่อส่วนต่าง ๆ ที่แอปต้องใช้ การแปลงเป็นเวกเตอร์ไม่รู้ว่าพื้นที่ใดควรชื่อ accent และไม่รับประกันว่าเอกสารนำเข้าจะเข้ากับโมเดลข้อมูลของโปรแกรมแก้ไข

เก็บไฟล์หลัก SVG ที่เหมาะสมเมื่อมีรูปร่างเหล่านั้นอยู่แล้ว ใช้ภาพรวมการแปลงเป็นเวกเตอร์ เพื่อตัดสินใจว่าจำเป็นต้องเตรียมราสเตอร์เป็นเวกเตอร์ก่อนเข้าโค้ดหรือไม่

คำถามที่พบบ่อย

SVG.js นำเข้าสตริง SVG เพื่อแก้ไขได้หรือไม่? ได้ ตัวอย่างนี้นำเข้ามาร์กอัปที่เชื่อถือได้ของตัวเองลงในคอนเทนเนอร์ SVG ที่ทำงานอยู่ เลือกพาธที่สร้างไว้ และเปลี่ยนสีเติม การอัปโหลดใด ๆ ต้องมีนโยบายความเชื่อถือและการตรวจสอบแยกต่างหาก API มาร์กอัปไม่ใช่ตัวกรองความปลอดภัย

ทำไมเห็นภาพแต่หาพาธ accent ไม่พบ? ตรวจว่าคุณนำเข้ามาร์กอัปที่ใช้งานอยู่หรือวางภาพ ชุดควบคุมตัวห่อภาพของเราแสดงงาน SVG แต่ไม่มีพาธ accent โดยตรงในต้นไม้ของตัวห่อ ตรวจองค์ประกอบแม่ที่เลือกและ ID จริงของต้นทางด้วย

การส่งออกเฉพาะองค์ประกอบลูกเป็นไฟล์ SVG ที่สมบูรณ์หรือไม่? ไม่ใช่ในชุดทดสอบนี้ เพราะมีพาธสองเส้นโดยไม่มี viewport SVG ชั้นนอก ใช้รูทเต็มที่ต้องการสำหรับไฟล์เดี่ยว หรือกำหนดรูทและระบบพิกัดปลายทางอย่างชัดเจนเมื่อใส่ส่วนย่อย

การแก้ไขสำเร็จพิสูจน์ว่าการส่งออกเก็บทุกความสามารถของ SVG หรือไม่? ไม่ เราตรวจพาธเรียบง่ายสองเส้น ค่าสีเติม ข้อมูลพาธที่ตรงกันทุกตัว และไฟล์ที่เปิดใหม่ห้าไฟล์ ทดสอบความสามารถเฉพาะและแอปรับงานที่งานของคุณต้องใช้ พร้อมเก็บไฟล์หลักต้นฉบับ

แหล่งข้อมูล

  1. SVG.js — การนำเข้า / ส่งออก รุ่น 3.2 — การนำเข้ามาร์กอัปและการแปลงรูทเต็มเทียบกับเฉพาะองค์ประกอบลูกเป็นข้อความ
  2. SVG.js — การอ้างอิง / สร้างองค์ประกอบ รุ่น 3.2 — การหาองค์ประกอบที่มีชื่อและเปลี่ยนคุณสมบัติ
  3. SVG.js — รุ่น 3.2.6 — รุ่นอย่างเป็นทางการที่ตรึงไว้ในตัวอย่างที่รัน
  4. MDN — SVG ในรูปภาพ — บริบทการฝังเป็นภาพและข้อจำกัด
  5. MDN — DOMParser.parseFromString — การแยกวิเคราะห์มาร์กอัปและการจัดการข้อมูลที่ไม่น่าเชื่อถือแยกกัน

หากภาพของคุณมีเพียงพิกเซลราสเตอร์และต้องแก้ไขเป็นส่วน ๆ ให้เตรียมเวกเตอร์ตัวเลือกจากงานของคุณเอง ตรวจเส้นรอบรูปและช่องว่าง เก็บไฟล์หลัก แล้วพิสูจน์การแก้ไขส่วนที่มีชื่อและ SVG ที่บันทึกในกระบวนการรับงานจริง

เพิ่มเติมจากบล็อก

PerfectVector

เริ่มต้นด้วย SVG ที่เรียบร้อยขึ้น
และแก้ไขได้ง่ายกว่า

ไม่ต้องใช้บัตรเครดิต