PerfectVector
Autor: Irene Kim

SVG.js: import grafiki, edycja ścieżki i eksport SVG

Importuj zaufane SVG do SVG.js, zmień kolor nazwanej ścieżki i porównaj eksport całego korzenia z eksportem dzieci. Zachowaj geometrię i sprawdź zapisane pliki.

9 min czytania
Na tej stronie

Aby zaimportować grafikę do SVG.js i edytować jej część, przekaż zaufane znaczniki SVG do .svg(), znajdź właściwy element po nadanym mu ID i zmień jego wypełnienie. Do samodzielnego pliku wyeksportuj cały korzeń SVG grafiki. Eksport samych dzieci jest fragmentem: zanim przekażesz go jako dokument, potrzebuje korzenia z zamierzonymi wymiarami i viewBox.

Ten poradnik wykorzystuje oryginalny pusty romb i osobny trójkąt. Trójkąt zmienia kolor z ochry na koralowy, a romb zachowuje turkusowe wypełnienie i otwór. Przetestowaliśmy poniższy kompletny przykład z SVG.js 3.2.6, przypiętym do oficjalnego wydania 3.2.6. Obserwacje dotyczą dwóch prostych, celowo utworzonych ścieżek, a nie wszystkich funkcji, jakie może zawierać inny SVG.

Ustal, czy potrzebujesz aktywnych kształtów, czy obrazu

Instrukcja importu JavaScript zwracająca URL zasobu rozwiązuje problem wczytywania. Sama nie umieszcza ścieżek pliku w aktywnym drzewie SVG edytora. Aktualna dokumentacja importu i eksportu SVG.js opisuje import znaczników i pobieranie zewnętrznego elementu SVG albo jego dzieci.

ReprezentacjaDo czego daje dostęp nasz przykładKontrola przed przekazaniem
Zaufane znaczniki zaimportowane do aktywnego korzenia SVGŚcieżka o nazwie accentSprawdź edytowany korzeń i ponownie otwórz jego eksport
SVG wczytany przez element obrazuElement obrazu jako całośćSprawdź oddzielnie zawartość obrazu
Znaczniki samych dzieciDwa elementy ścieżek bez oryginalnego korzeniaZapewnij świadomie dobrany korzeń, wymiary i viewBox

SVG wewnątrz obrazu nadal jest treścią SVG. Jednak w naszym wariancie kontrolnym z obrazem wewnątrz kontenera otaczające drzewo SVG ma jeden element obrazu i żadnych bezpośrednich ścieżek; wyszukiwanie accent w tym drzewie niczego nie znajduje. Nie sięga do dokumentu osadzonego obrazu. MDN opisuje oddzielny kontekst SVG jako obrazu i jego ograniczenia. Nie wnioskuj o modelu edycji z ostrego wyglądu renderowania.

Szerszą decyzję o osadzaniu omawia SVG inline a obraz. Jeśli wolisz model obiektowy canvas biblioteki Fabric, poradnik importu Fabric.js opisuje inny parser i API edycji.

Edytuj jedną nazwaną ścieżkę i zapisz każdą reprezentację

Zapisz cały poniższy kod jako starter.html w pustym folderze. Udostępnij go przez HTTP, na przykład za pomocą python3 -m http.server 8000, a następnie otwórz http://localhost:8000/starter.html. Skrypt wczytuje przypięty pakiet z CDN, więc przykład wymaga połączenia z siecią.

Strona pokazuje oryginalną grafikę, aktywną edytowaną kopię i wariant kontrolny z obrazem w kontenerze. Linki pobierają oryginalny plik główny, pełne korzenie przed i po edycji, tekstowy fragment samych dzieci, kopię z ponownie dodanym korzeniem i kontener obrazu. Raport odczytuje piksele obrazów SVG narysowanych na canvas; nie eksportuje 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>
 

Zaimportowane znaczniki zawierają zagnieżdżony korzeń o nazwie original-artwork. Przykład wybiera ten korzeń przed eksportem, dzięki czemu kopia do przekazania zachowuje zaprojektowany viewport 240 × 160, zamiast polegać na niepowiązanym kontenerze nadrzędnym. Następnie wybiera accent wewnątrz tego korzenia. SVG.js dokumentuje odwołania za pomocą selektorów i metody elementów; jawne nazwy części pozwalają uniknąć zależności od kolejności ścieżek.

Kod jest celowo ograniczony do własnych stałych znaczników. Nie jest narzędziem oczyszczającym przesyłane SVG. Parsowanie lub wstawienie dowolnego ciągu nie dowodzi jego bezpieczeństwa; dokumentacja DOMParser w MDN odróżnia parsowanie od zagrożeń związanych ze wstawianiem niezaufanej treści. Zanim przystosujesz tę funkcję do przesyłanych plików, zaprojektuj oddzielne zasady walidacji i zaufania.

Sprawdzaj zapisany wynik, a nie tylko aktywny podgląd

Rzeczywiste uruchomienie w przeglądarce dało następujące wyniki:

Zapisana reprezentacjaŚcieżki w drzewie dokumentuObrazyWypełnienie trójkąta
Oryginalny plik główny20#d4a53a
Pełny korzeń przed edycją20#d4a53a
Pełny korzeń po edycji20#b95434
Dzieci umieszczone w nowym pełnym korzeniu20#b95434
Kontener obrazu01Oryginalna ochra wewnątrz zawartości obrazu

Oba oryginalne ciągi d ścieżek pozostały identyczne w eksportach przed i po zmianie. Próbka wnętrza trójkąta zmieniła się z RGBA (212, 165, 58, 255) na (185, 84, 52, 255). Próbka wnętrza rombu pozostała (23, 107, 91, 255); jego otwór i zewnętrzny narożnik zachowały alfę 0. Wszystkie pięć samodzielnych plików SVG otworzyło się ponownie jako obrazy o naturalnych wymiarach 240 × 160.

Zmień jedną nazwaną część
Schematyczne porównanie jednakowych pustych rombów, w którym tylko osobny trójkąt zmienia kolor ze złotego na koralowy
Schematyczna ilustracja zmiany koloru jednej części. Ozdobne granice i strzałki są wskazówkami diagramu; rzeczywistych dowodów dostarczają uruchomiony przykład, inwentaryzacja zapisanego źródła i kontrola pikseli w przeglądarce.

Wyniki potwierdzają zamierzoną edycję dla tego zestawu testowego. Nie potwierdzają zachowania XML identycznego bajt po bajcie, obsługi wszystkich filtrów lub CSS ani przydatności w procesie produkcyjnym. Serializacja biblioteki może zmienić znaczniki dokumentu, pozostawiając te konkretne ciągi ścieżek nietknięte. Zachowaj oryginalny plik główny obok kopii do przekazania.

We własnej grafice sprawdź struktury, których rzeczywiście używasz: nazwy i grupy, przycinanie, gradienty, transformacje, obrysy i tekst. Otwórz wyeksportowany plik w aplikacji odbiorcy i powtórz zamierzoną edycję. Poradnik edycji SVG może pomóc rozpoznać strukturę źródła przed zmianą kodu importu.

Traktuj eksport samych dzieci jako świadomie wybrany fragment

W tym uruchomieniu artwork.svg(false) zwróciło dwie ścieżki bez zewnętrznego znacznika svg. Pominęło viewport korzenia grafiki. Oddzielny korzeń SVG rewrapped zapewnia wymiary 240 × 160 i ten sam viewBox przed przyjęciem dzieci; jego wyeksportowany plik renderuje następnie sprawdzony koralowy trójkąt.

Wybierz pełny dokument lub fragment zależnie od miejsca docelowego. Do samodzielnego pobrania zachowaj cały zamierzony korzeń. Wstawiając fragment do istniejącego rysunku, ustal, z jakiego nadrzędnego układu współrzędnych, dziedziczonych stylów i definicji będą korzystać dzieci. Ten prosty zestaw nie ma zasobów zewnętrznych ani wspólnych definicji, więc nie sprawdza, czy fragment zawierający odwołania przenosi wszystkie potrzebne zależności.

Eksport naszego kontenera obrazu zachował zakodowany SVG, którego zdekodowane bajty odpowiadały oryginalnemu plikowi głównemu. Ten wariant kontrolny pokazuje różnicę reprezentacji, a nie konwersję rastrową. Jeśli źródło zawiera już użyteczną geometrię wektorową, popraw wybór elementu lub granicę eksportu przed ponownym trasowaniem.

Gdy źródło nie ma ścieżek do edycji

Jeśli twoja płaska ilustracja PNG lub JPG wymaga obszarów, którym można osobno zmieniać kolory, przygotuj kandydat SVG za pomocą PerfectVector. Sprawdź kontury i otwory, a potem nadaj częściom nazwy potrzebne aplikacji. Wektoryzacja nie wie, że dany obszar powinien nazywać się accent, ani nie gwarantuje dopasowania importowanego dokumentu do modelu danych edytora.

Zachowaj odpowiedni plik główny SVG, jeśli te kształty już istnieją. Wprowadzenie do wektoryzacji pomoże ustalić, czy przygotowanie rastra do postaci wektorowej w ogóle powinno poprzedzać twój kod.

FAQ

Czy SVG.js może importować ciąg SVG do edycji? Tak. Ten przykład importuje własne zaufane znaczniki do aktywnego kontenera SVG, wybiera utworzoną ścieżkę i zmienia jej wypełnienie. Dowolne przesyłane pliki wymagają oddzielnych zasad zaufania i walidacji; API znaczników nie jest narzędziem oczyszczającym.

Dlaczego widzę grafikę, ale nie znajduję jej ścieżki accent? Sprawdź, czy zaimportowano aktywne znaczniki, czy umieszczono obraz. Nasz wariant kontrolny z kontenerem obrazu wyświetla grafikę SVG, ale nie udostępnia bezpośredniej ścieżki accent w drzewie kontenera. Sprawdź też wybranego rodzica i rzeczywiste ID w źródle.

Czy eksport samych dzieci jest kompletnym plikiem SVG? Nie w tym zestawie. Zawiera dwie ścieżki bez zewnętrznego viewportu SVG. Dla samodzielnego pliku użyj zamierzonego pełnego korzenia albo jawnie zapewnij docelowy korzeń i układ współrzędnych przy wstawianiu fragmentu.

Czy udana edycja dowodzi, że eksport zachowuje każdą funkcję SVG? Nie. Sprawdziliśmy dwie proste ścieżki, ich wartości wypełnienia, dokładne dane ścieżek i pięć ponownie otwartych plików. Przetestuj konkretne funkcje i aplikację odbiorcy potrzebne twojej grafice oraz zachowaj oryginalny plik główny.

Źródła

  1. SVG.js — import i eksport, wersja 3.2 — import znaczników oraz serializacja całego korzenia lub samych dzieci.
  2. SVG.js — odwołania i tworzenie elementów, wersja 3.2 — znajdowanie nazwanych elementów i zmienianie ich właściwości.
  3. SVG.js — wydanie 3.2.6 — oficjalne wydanie przypięte w uruchomionym przykładzie.
  4. MDN — SVG jako obraz — kontekst osadzania obrazu i jego ograniczenia.
  5. MDN — DOMParser.parseFromString — parsowanie znaczników i oddzielna obsługa niezaufanych danych wejściowych.

Jeśli ilustracja istnieje tylko jako piksele rastrowe i wymaga edycji poszczególnych części, przygotuj kandydat wektorowy z własnej grafiki. Sprawdź obrys i otwory, zachowaj plik główny, a potem potwierdź edycję nazwanej części i zapisany SVG w docelowym procesie pracy.

Więcej z bloga

PerfectVector

Zacznij od czystszego SVG,
który łatwiej edytować

Bez karty kredytowej