PerfectVector
โดย Irene Kim

ภาพ SVG ในโหนด Cytoscape.js: แก้ขนาดและช่องว่าง

ใส่ภาพ SVG ในโหนด Cytoscape.js ด้วยตัวอย่างใช้งานจริง เทียบช่องว่างต้นฉบับ contain และ cover แล้วตรวจการโหลดและตำแหน่งป้ายข้อความ

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

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

Cytoscape.js มีเอกสารพื้นหลังโหนด SVG รวมการปรับแบบ contain, cover และ none คู่มือนี้ใช้ตราต้นฉบับหนึ่งเส้นเพื่อแยกขอบเขตต้นทางออกจากสไตล์กราฟ เก็บต้นฉบับภาพที่แก้ไขได้ไว้กับการตั้งค่ากราฟ

แยกภาพต้นฉบับออกจากโหนดกราฟ

มีกรอบสามกรอบต้องตรวจ ได้แก่ ขอบเขตภาพที่วาด viewport ภาพ SVG และตัวโหนด viewBox ของ SVG กำหนดสี่เหลี่ยมพิกัดต้นทางที่แมปเข้า viewport ซึ่งอาจรวมพื้นที่มากกว่าภาพที่เห็นมาก

ต้นฉบับขอบกระชับของเรามีขนาด 120 × 60 โดยตราปะการังครอบคลุม x=6 ถึง x=114 และ y=6 ถึง y=54 ช่องกลางเป็นส่วนของเส้นเดียวกัน:

<?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>

เวอร์ชันเผื่อพื้นที่เปลี่ยนเฉพาะขนาดนอก SVG และ viewBox:

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

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

รันตัวอย่างสี่กรณี

เราทดสอบ Cytoscape.js 3.34.3 ด้วยชุดเบราว์เซอร์ทางการที่ตรึงรุ่นและให้บริการในเครื่อง บันทึกชุดจากแท็กรุ่นนั้น เป็น cytoscape-3.34.3.min.js ข้าง example.html ด้านล่าง ให้บริการโฟลเดอร์ผ่าน HTTP ในเครื่อง เช่น python3 -m http.server 8000 แล้วเปิด http://localhost:8000/example.html

ตัวอย่างเข้ารหัส SVG ต้นฉบับเป็น data URI encodeURIComponent() escape อักขระ URI รวม # ในสีเติมของเรา คำแนะนำ SVG ของไลบรารีแนะนำการเข้ารหัสนี้แทน Base64 และส่วนหัว XML ที่แสดงที่นี่ นี่เป็นตราที่อยู่ครบในตัว ไม่มีฟอนต์หรือภาพภายนอก

<!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>

ส่วนสถานะรายงานขนาดภาพที่โหลดล่วงหน้าก่อนเริ่มกราฟ naturalWidth อ่านความกว้างตามธรรมชาติที่ปรับความหนาแน่นแล้วของภาพเป็นพิกเซล CSS ตามคำอธิบาย MDN ในไฟล์ทดสอบนี้ ภาพขอบกระชับรายงาน 120 × 60 และภาพเผื่อพื้นที่รายงาน 200 × 140 นั่นเป็นขนาดภาพ ไม่ใช่ค่าที่วัดเส้นปะการัง

เทียบสิ่งที่เปลี่ยนเมื่อขนาดโหนดเท่ากัน

โหนดแบบจำลองทั้งสี่มีขนาด 120 × 80 และ padding โหนดเป็นศูนย์ เราตรวจไฟล์ต้นฉบับใน Chrome ที่ซูมกราฟ 1 และ 1.5 สไตล์โหนดยังเป็น 120 × 80 แต่ขนาดเรนเดอร์ที่รายงานเป็น 180 × 120 ที่ 1.5 นี่เป็นการเทียบคงที่ในเบราว์เซอร์นั้น ไม่ใช่การทดสอบความเข้ากันได้หรือประสิทธิภาพซูม

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

ต้นฉบับขอบกระชับมีอัตราส่วนภาพ 2:1 แต่โหนดเป็น 1.5:1 การใส่ภาพ 120 × 60 ทั้งภาพใน 120 × 80 เหลือพื้นที่แนวตั้ง การใช้ cover เต็มโหนดจะปรับภาพเป็น 160 × 80 ก่อนตัด ทำให้ปลายแนวนอนยื่นนอกตัวโหนด

สำหรับต้นฉบับเผื่อพื้นที่ กรอบภาพที่ใช้ได้รวมพื้นที่เพิ่มด้านละ 40 หน่วย เมื่อใช้ contain สเกลถูกจำกัดด้วย 80/140 แทน 120/200 ตรากว้าง 108 หน่วยเดิมจึงกินพื้นที่ประมาณ 62 หน่วยแบบจำลอง ค่านี้คำนวณจากขนาดที่ประกาศ ไม่ใช่วัดพิกเซลจากภาพหน้าจอ

ขอบเขตต้นฉบับและการปรับภาพ
ภาพอธิบายตราเล็กที่มีพื้นที่ว่างต้นฉบับมาก ตราใหญ่ที่อยู่ครบในกรอบโหนด และตราที่ปลายถูกตัดด้วยกรอบแคบกว่า
ภาพอธิบาย: ช่องว่างต้นฉบับและการปรับภาพเปลี่ยนภาพที่เห็นได้อย่างอิสระ แผนภาพแนวคิดนี้ไม่ใช่ภาพหน้าจอ Cytoscape หรือผลแปลง

เลือกวิธีปรับหลังตรวจขอบเขตภาพ

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

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

คง background-width และ background-height เป็น auto สำหรับการเปรียบเทียบนี้ ค่าที่ระบุเองอาจเขียนทับขนาดภาพที่ใช้ปรับ padding โหนดเป็นอีกอินพุตหนึ่ง ไลบรารีระบุว่าขนาดพื้นหลังรวมมันหรือใช้ตัวด้านใน ดูเอกสาร background-image ก่อนเพิ่มตัวควบคุมเหล่านั้น ไฟล์ทดสอบ padding ศูนย์ของเราแยกช่องว่างต้นฉบับก่อน

สำหรับการตัดสิน meet, slice และการจัดแนวระดับ SVG ไปต่อที่คู่มือ preserveAspectRatio การเปลี่ยนวิธีปรับภายใน SVG กับวิธีปรับพื้นหลัง Cytoscape เป็นคนละงาน

วินิจฉัยการโหลดก่อนวินิจฉัยการตัดขอบ

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

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

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

แยกป้ายข้อความกับภาพที่แก้ไขได้

ตัวอย่างใช้ label: 'data(label)' สำหรับป้ายกราฟและไม่มีข้อความ SVG คุณเปลี่ยนข้อความได้โดยไม่สร้างตราใหม่ เส้น SVG ยังอยู่ในต้นฉบับหลัก ขณะที่กราฟจัดการตัวตน ตำแหน่ง และความสัมพันธ์ของโหนด

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

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

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

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

ควรใช้ contain หรือ cover กับตราเอง? เลือก contain เมื่อตราครบสำคัญ เลือก cover เฉพาะเมื่อการตัดขอบที่เห็นยอมรับได้สำหรับงาน และตรวจด้วยต้นฉบับกับรูปโหนดของคุณเอง

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

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

แหล่งข้อมูล

  1. Cytoscape.js — ภาพพื้นหลัง — สไตล์ URL ภาพ การปรับภาพ การเขียนทับขนาด การอ้างอิง padding และประเด็น SVG
  2. Cytoscape.js — รุ่น v3.34.3 — รุ่นที่ตรึงสำหรับไฟล์ทดสอบต้นฉบับ
  3. Cytoscape.js — ชุดเบราว์เซอร์ v3.34.3 — โค้ดทางการตามแท็กที่ให้บริการในเครื่องสำหรับทดสอบเบราว์เซอร์
  4. MDN — viewBox — สี่เหลี่ยมพิกัดต้นทางและการแมป viewport
  5. MDN — encodeURIComponent — การ escape ส่วน SVG ใน data URI
  6. MDN — naturalWidth — ขนาดภาพที่โหลด ซึ่งต่างจากขอบเขตเส้น
  7. MDN — SVG เป็นภาพ — ข้อจำกัดบริบทภาพและการตรวจการพึ่งพา

หากตราเริ่มเป็นแรสเตอร์ ให้สร้างต้นฉบับ SVG ที่แก้ไขได้ ตรวจเส้นขอบและขอบเขตต้นทาง แล้วเทียบ contain และ cover ในเค้าโครงโหนดของคุณเอง

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

PerfectVector

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

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