PerfectVector
Autor: Irene Kim

Obrazy SVG w węzłach Cytoscape.js: dopasowanie i marginesy

Dopasuj własną grafikę SVG do węzłów Cytoscape.js na działającym przykładzie. Porównaj puste miejsce, contain i cover, potem sprawdź ładowanie oraz położenie etykiet.

9 min czytania
Na tej stronie

Użyj adresu obrazu SVG w stylu background-image węzła, a potem wybierz dopasowanie do grafiki, którą chcesz pokazać. Jeśli znak ładuje się, lecz wygląda na maleńki, sprawdź puste miejsce w viewporcie SVG przed powiększeniem węzła grafu. Jeśli końce znikają, porównaj proporcje obrazu z węzłem i sprawdź przycinanie.

Cytoscape.js opisuje tła SVG węzłów, w tym dopasowania contain, cover i none. Ten poradnik używa naszego oryginalnego znaku z jedną ścieżką, aby oddzielić granice źródła od stylu grafu. Zachowaj edytowalną wersję wzorcową grafiki obok konfiguracji grafu.

Oddziel obraz źródłowy od węzła grafu

Trzeba sprawdzić trzy prostokąty: granice narysowanej grafiki, viewport obrazu SVG i korpus węzła. viewBox SVG definiuje prostokąt współrzędnych źródłowych mapowany do viewportu. Może obejmować znacznie więcej miejsca niż widoczny rysunek.

Nasze źródło z małymi marginesami ma 120 × 60, a koralowy znak rozciąga się od x=6 do x=114 i od y=6 do y=54. Otwór w środku jest częścią tej samej ścieżki:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg>
<svg xmlns="http://www.w3.org/2000/svg"
     width="120" height="60" viewBox="0 0 120 60">
  <path fill="#f36f59" fill-rule="evenodd"
    d="M6 30L30 6H90L114 30L90 54H30Z M50 20H70V40H50Z"/>
</svg>

Wariant z większymi marginesami zmienia tylko zewnętrzne wymiary SVG i viewBox:

<svg xmlns="http://www.w3.org/2000/svg"
     width="200" height="140" viewBox="-40 -40 200 140">

Zachowaj ten sam nagłówek, ścieżkę i znacznik zamykający. Oba pliki rysują identyczne współrzędne ścieżki. Drugi otacza je większą ilością pustego miejsca w źródle. Miejsce skaluje się z obrazem, więc zmiana dopasowania tła nie zastępuje niezawodnie wyboru odpowiednich granic źródła. Poradnik obwiedni SVG pomaga, gdy viewport pliku i widoczna grafika się nie zgadzają.

Uruchom przykład czterech przypadków

Testowaliśmy Cytoscape.js 3.34.3 z oficjalnym pakietem przeglądarkowym przypiętym do tej wersji, serwowanym lokalnie. Zapisz pakiet z tego tagu wydania jako cytoscape-3.34.3.min.js obok poniższego example.html. Udostępnij folder przez lokalne HTTP, na przykład python3 -m http.server 8000, i otwórz http://localhost:8000/example.html.

Przykład koduje oryginalny SVG do URI danych. encodeURIComponent() koduje znaki URI, w tym # w naszym kolorze wypełnienia. Wskazówki SVG biblioteki zalecają to kodowanie zamiast Base64 oraz pokazany tutaj nagłówek XML. To jeden samowystarczalny znak bez zewnętrznych fontów lub obrazów.

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>SVG node fit comparison</title>
<style>
  body { font: 16px system-ui; margin: 20px; }
  #cy { width: 1100px; height: 250px; border: 1px solid #ddd; }
  #status { white-space: pre-wrap; }
</style>
<div id="cy"></div>
<pre id="status">Loading images</pre>
<script src="cytoscape-3.34.3.min.js"></script>
<script>
const header = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg>';
const path = 'M6 30L30 6H90L114 30L90 54H30Z M50 20H70V40H50Z';
function artwork(padded) {
  const width = padded ? 200 : 120;
  const height = padded ? 140 : 60;
  const viewBox = padded ? '-40 -40 200 140' : '0 0 120 60';
  return header + `<svg xmlns="http://www.w3.org/2000/svg"
    width="${width}" height="${height}" viewBox="${viewBox}">
    <path fill="#f36f59" fill-rule="evenodd" d="${path}"/></svg>`;
}
const cases = [
  ['tight contain', false, 'contain'],
  ['tight cover', false, 'cover'],
  ['padded contain', true, 'contain'],
  ['padded cover', true, 'cover']
].map(([label, padded, fit]) => ({
  label, fit,
  image: 'data:image/svg+xml;utf8,' + encodeURIComponent(artwork(padded))
}));
Promise.all(cases.map(item => new Promise((resolve, reject) => {
  const image = new Image();
  image.onload = () => resolve({
    label: item.label,
    width: image.naturalWidth,
    height: image.naturalHeight
  });
  image.onerror = () => reject(new Error(item.label + ' failed to load'));
  image.src = item.image;
}))).then(loaded => {
  cytoscape({
    container: document.getElementById('cy'),
    elements: cases.map((item, index) => ({
      data: { id: 'n' + index, ...item },
      position: { x: 110 + index * 260, y: 95 }
    })),
    style: [{ selector: 'node', style: {
      width: 120, height: 80, padding: 0, shape: 'rectangle',
      'background-color': '#f1f3f5',
      'background-image': 'data(image)',
      'background-fit': node => node.data('fit'),
      'background-width': 'auto', 'background-height': 'auto',
      'background-width-relative-to': 'inner',
      'background-height-relative-to': 'inner',
      'background-position-x': '50%', 'background-position-y': '50%',
      'background-repeat': 'no-repeat', 'background-clip': 'node',
      'border-width': 1, 'border-color': '#222',
      label: 'data(label)', 'text-valign': 'bottom',
      'text-halign': 'center', 'text-margin-y': 10, 'font-size': 12
    }}],
    layout: { name: 'preset', fit: false },
    zoom: 1, pan: { x: 0, y: 0 },
    userZoomingEnabled: false, userPanningEnabled: false,
    boxSelectionEnabled: false
  });
  document.getElementById('status').textContent =
    'Cytoscape ' + cytoscape.version + '\n' +
    JSON.stringify(loaded, null, 2);
}).catch(error => {
  document.getElementById('status').textContent = error.message;
});
</script>
</html>

Blok stanu podaje wymiary obrazów wstępnie wczytanych przed inicjalizacją grafu. naturalWidth odczytuje wewnętrzną szerokość obrazu skorygowaną o gęstość w pikselach CSS, zgodnie z opisem MDN. W tym teście obrazy z małymi marginesami podawały 120 × 60, a z większymi 200 × 140. To wymiary obrazów, a nie pomiary koralowej ścieżki.

Porównaj zmiany przy tym samym rozmiarze węzła

Wszystkie cztery węzły modelu mają 120 × 80 i zerowy wewnętrzny margines. Sprawdziliśmy oryginalny przykład w Chrome przy powiększeniu grafu 1 i 1.5. Styl węzła pozostał 120 × 80; raportowane wymiary renderowane wyniosły 180 × 120 przy 1.5. Było to statyczne porównanie w tej przeglądarce, a nie test zgodności lub wydajności powiększania.

Źródło i dopasowanieWygląd zaobserwowany w tym teście
Małe marginesy, containCały znak z widocznymi spiczastymi końcami
Małe marginesy, coverWiększy znak z lewą i prawą końcówką przyciętą na granicy węzła
Duże marginesy, containMniejszy znak otoczony znaczną pustą przestrzenią
Duże marginesy, coverNieco większy niż poprzedni contain, nadal znacznie mniejszy niż przypadki z małymi marginesami

Źródło z małymi marginesami ma proporcje obrazu 2:1, a węzeł 1.5:1. Umieszczenie całego obrazu 120 × 60 wewnątrz 120 × 80 pozostawia miejsce w pionie. Pokrycie węzła skaluje obraz do 160 × 80 przed przycięciem, więc jego poziome końce wychodzą poza korpus.

W źródle z większymi marginesami dostępny prostokąt obrazu obejmuje dodatkowe 40 jednostek miejsca z każdej strony. Przy contain skalę ogranicza 80/140, a nie 120/200. Niezmieniony znak o szerokości 108 jednostek zajmuje więc około 62 jednostek modelu. To obliczenie z zadeklarowanych wymiarów, a nie pikselowy pomiar zrzutu ekranu.

Granice źródła i dopasowanie
Ilustracja małego znaku z dużym pustym marginesem źródłowym, całego większego znaku w ramce węzła oraz znaku z końcami przyciętymi przez węższą ramkę.
Ilustracja: puste miejsce źródła i dopasowanie mogą niezależnie zmieniać widoczną grafikę. Schemat koncepcyjny nie jest zrzutem Cytoscape ani wynikiem konwersji.

Wybierz dopasowanie po sprawdzeniu granic grafiki

Zacznij od źródła z małymi marginesami i contain, gdy ważny jest cały znak. Zachowaj dość miejsca dla konturu lub efektu, który musi pozostać widoczny. Zbyt ciasny viewport może przyciąć właściwą grafikę; nie jest to nakaz usunięcia każdego marginesu.

Używaj cover, gdy przycięcie krawędzi jest akceptowalne dla projektu. W naszym przykładzie ukrywa spiczaste końce, więc jest złym wyborem, jeśli identyfikują znak. Szerszy węzeł może zmienić proporcje dopasowania i pokazać końce, ale zmienia też układ grafu. Proporcjonalne powiększenie obu wymiarów zachowuje to samo przycięcie. Ustal, które ograniczenie chcesz zmienić.

W tym porównaniu pozostaw background-width i background-height na auto. Jawne wartości mogą nadpisać wymiary obrazu używane do dopasowania. padding węzła jest kolejnym oddzielnym parametrem; biblioteka opisuje, czy wymiarowanie tła go obejmuje, czy używa korpusu wewnętrznego. Zobacz opis background-image przed dodaniem tych ustawień. Nasz przykład z zerowym marginesem wewnętrznym najpierw izoluje puste miejsce źródła.

Decyzje o meet, slice i wyrównaniu na poziomie SVG opisuje poradnik preserveAspectRatio. Zmiana wewnętrznego dopasowania SVG i zmiana dopasowania tła Cytoscape to oddzielne operacje.

Diagnozuj ładowanie przed przycinaniem

Jeśli stan zgłasza błąd ładowania, zmiana contain na cover nie jest następnym krokiem. Otwórz sam zasób obrazu, sprawdź jego adres i zadeklarowane wymiary, a potem uprość go do samowystarczalnego znaku z samymi ścieżkami powyżej. Dla URI danych zakoduj cały ciąg SVG, zamiast łączyć surowy kod znaczników lub niezakodowany kolor.

SVG używany jako obraz ma inne ograniczenia niż SVG otwarty jako dokument. Poradnik MDN o SVG jako obrazie opisuje ograniczenia skryptów i zewnętrznych zasobów w kontekstach obrazowych. Grafika zależna od zewnętrznego arkusza stylów lub fontu wymaga osobnego testu przekazania. Ten przykład unika tych zależności i umieszcza etykiety grafu poza SVG.

Jeśli obraz się ładuje, sprawdź dopasowanie i przycinanie węzła z wyraźnie szerokim kształtem. Potem przetestuj prawdziwą grafikę przy rozmiarze węzła i w środowiskach przeglądarkowych obsługiwanych przez aplikację. Dokumentacja Cytoscape wskazuje kwestie SVG zależne od przeglądarki; ta obserwacja w Chrome nie rozstrzyga ich dla Firefoksa lub każdego innego silnika.

Oddziel etykiety od edytowalnej grafiki

Przykład używa label: 'data(label)' dla etykiet grafu i nie zawiera tekstu SVG. Możesz zmienić słowa bez przebudowy znaku. Ścieżka SVG pozostaje we wzorcu źródłowym, a graf zarządza tożsamością, położeniem i relacjami węzłów.

Badany przykład rysował na powierzchniach Canvas. Widoczne tam tło SVG nie zamienia jego wewnętrznych ścieżek w oddzielnie edytowalne elementy grafu. Zachowaj wzorzec, jeśli chcesz zmienić otwór, sylwetkę lub kolory. Eksport grafu to osobny proces wymagający wybranego eksportera i własnego testu przekazania; poradnik nie gwarantuje zachowania edytowalnych ścieżek znaku w eksportowanym grafie. Poradnik grafiki Graphviz omawia przekazanie odwołań do obrazów w innym rendererze diagramów.

Jeśli własny znak istnieje tylko jako PNG lub JPG, przekonwertuj to źródło na grafikę wektorową, a potem sprawdź sylwetkę, otwór i małe detale przed ustawieniem granic SVG. PerfectVector może pomóc odzyskać edytowalną geometrię źródłową. Style tła, etykiety węzłów i eksport grafu pozostają oddzielnymi etapami. Istniejący odpowiedni SVG może przejść od razu do kontroli dopasowania.

Najczęstsze pytania

Dlaczego obraz SVG węzła jest mały nawet przy cover? Obraz źródłowy może zawierać dużo pustego miejsca viewportu wokół ścieżki. Porównaj kopię z ciaśniejszymi granicami przy tych samych wymiarach węzła przed zmianą układu grafu.

Czy wybrać contain czy cover dla własnego znaku? Wybierz contain, gdy ważny jest cały znak. Wybieraj cover tylko wtedy, gdy obserwowane przycięcie krawędzi jest akceptowalne, i sprawdź to z własnym źródłem oraz kształtem węzła.

Czy tło SVG węzła tworzy edytowalne ścieżki grafu? Testowany przykład Canvas używa SVG jako malowanego obrazu. Zachowaj wzorzec grafiki do edycji ścieżek; jej wewnętrzne kontury nie są w tym przykładzie osobnymi węzłami ani krawędziami grafu.

Czy mogę umieścić etykiety w SVG? Ten przykład zachowuje etykiety w danych grafu i używa grafiki z samych ścieżek. Tekst SVG, fonty i zależności kontekstu obrazu wymagają osobnych kontroli przeglądarek i przekazania.

Źródła

  1. Cytoscape.js — Obraz tła — style z adresem obrazu, dopasowanie, nadpisanie wymiarów, odniesienia marginesów wewnętrznych i kwestie SVG.
  2. Cytoscape.js — Wydanie v3.34.3 — przypięte wydanie użyte w oryginalnym teście.
  3. Cytoscape.js — Pakiet przeglądarkowy v3.34.3 — oficjalna implementacja z tagiem udostępniona lokalnie do testu przeglądarkowego.
  4. MDN — viewBox — prostokąt współrzędnych źródłowych i mapowanie viewportu.
  5. MDN — encodeURIComponent — kodowanie składnika SVG w URI danych.
  6. MDN — naturalWidth — wymiary załadowanego obrazu, odrębne od granic ścieżki.
  7. MDN — SVG jako obraz — ograniczenia kontekstu obrazu i kontrole zależności.

Jeśli znak zaczyna się od rastra, utwórz edytowalny wzorzec SVG, sprawdź kontury i granice źródła, a potem porównaj contain i cover we własnym układzie węzłów.

Więcej z bloga

PerfectVector

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

Bez karty kredytowej