ภาพ SVG ในโหนด Cytoscape.js: แก้ขนาดและช่องว่าง
ใส่ภาพ SVG ในโหนด Cytoscape.js ด้วยตัวอย่างใช้งานจริง เทียบช่องว่างต้นฉบับ contain และ cover แล้วตรวจการโหลดและตำแหน่งป้ายข้อความ
ในหน้านี้
ใช้ 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 หน่วยแบบจำลอง ค่านี้คำนวณจากขนาดที่ประกาศ ไม่ใช่วัดพิกเซลจากภาพหน้าจอ

เลือกวิธีปรับหลังตรวจขอบเขตภาพ
เริ่มต้นฉบับขอบกระชับและ 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 ฟอนต์ และการพึ่งพาบริบทภาพต้องตรวจเบราว์เซอร์กับการส่งแยก
แหล่งข้อมูล
- Cytoscape.js — ภาพพื้นหลัง — สไตล์ URL ภาพ การปรับภาพ การเขียนทับขนาด การอ้างอิง padding และประเด็น SVG
- Cytoscape.js — รุ่น v3.34.3 — รุ่นที่ตรึงสำหรับไฟล์ทดสอบต้นฉบับ
- Cytoscape.js — ชุดเบราว์เซอร์ v3.34.3 — โค้ดทางการตามแท็กที่ให้บริการในเครื่องสำหรับทดสอบเบราว์เซอร์
- MDN — viewBox — สี่เหลี่ยมพิกัดต้นทางและการแมป viewport
- MDN — encodeURIComponent — การ escape ส่วน SVG ใน data URI
- MDN — naturalWidth — ขนาดภาพที่โหลด ซึ่งต่างจากขอบเขตเส้น
- MDN — SVG เป็นภาพ — ข้อจำกัดบริบทภาพและการตรวจการพึ่งพา
หากตราเริ่มเป็นแรสเตอร์ ให้สร้างต้นฉบับ SVG ที่แก้ไขได้ ตรวจเส้นขอบและขอบเขตต้นทาง แล้วเทียบ contain และ cover ในเค้าโครงโหนดของคุณเอง
เพิ่มเติมจากบล็อก

หมุด SVG ใน Leaflet: จัดปลายด้วย iconAnchor
วางหมุด SVG ใน Leaflet โดยแมปปลายที่เห็นกับ iconAnchor คำนึงถึงขอบว่าง viewBox การปรับสัดส่วน และการเปลี่ยนขนาดไอคอน

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