SVG feMorphology: เพิ่มขอบโดยไม่สร้างพาธใหม่
สร้างขอบ SVG จาก SourceAlpha ด้วย feMorphology เปรียบเทียบการขยาย การหด ช่องว่าง และรัศมีที่ไม่เท่ากัน โดยคงเรขาคณิตของพาธต้นฉบับไว้
ในหน้านี้
หากต้องการเพิ่มขอบรอบรูปเงาของ SVG หลายสีทั้งภาพ ให้ขยาย SourceAlpha ลบอัลฟาต้นฉบับออกจากผลลัพธ์ที่ใหญ่ขึ้น เติมสีให้วงขอบที่เหลือ แล้วซ้อนภาพต้นฉบับไว้ด้านบน ฟิลเตอร์จะเปลี่ยนรูปลักษณ์ที่เรนเดอร์ โดยข้อมูลพาธต้นฉบับยังคงเดิม
ใช้ operator="erode" หากต้องการรูปเงาจากอัลฟาที่เล็กลงแทน ทั้งสองวิธีไม่ได้สร้างพาธออฟเซ็ตที่แก้ไขได้ ต้องตรวจช่องว่าง ช่องแคบ และมุมคมด้วยตา เพราะการดำเนินการทางสัณฐานวิทยาใช้บริเวณใกล้เคียงรูปสี่เหลี่ยมผืนผ้า ไม่ใช่การออฟเซ็ตเส้นขอบแบบวงกลม
เริ่มจากรูปเงา ไม่ใช่ช่องสี
ข้อกำหนด Filter Effects ของ W3C นิยามการขยายว่าเป็นการหาค่าสูงสุดของแต่ละองค์ประกอบภายในบริเวณใกล้เคียงรูปสี่เหลี่ยมผืนผ้า และการหดว่าเป็นการหาค่าต่ำสุด เมื่อใช้ SourceAlpha ข้อมูลขาเข้าคือช่องอัลฟาของภาพ จึงสร้างรูปเงาได้โดยไม่ต้องขยายช่องสีเขียวอมฟ้าและสีคอรัลแยกกัน
ตัวอย่างนี้ใช้ฟิลเตอร์กับภาพทั้งสองพาธ ช่องสี่เหลี่ยมจัตุรัสโปร่งใสเป็นส่วนหนึ่งของรูปเงาจากอัลฟา ส่วนสีคอรัลอยู่ภายในบริเวณสีเขียวอมฟ้าที่ทึบอยู่แล้ว จึงไม่ได้สร้างขอบด้านนอกแยกต่างหาก
เราสร้าง SVG ด้วยมือและเปิดไฟล์อิสระห้ารูปแบบในเบราว์เซอร์ ทุกไฟล์คงนิยามพาธต้นฉบับสองพาธไว้เหมือนกัน ภาพเปรียบเทียบด้านล่างเป็นการเรนเดอร์ไฟล์เหล่านั้นจริง ไม่ได้ใช้ภาพที่ไล่เส้นหรือภาพตัวอย่างเอฟเฟกต์ที่สร้างขึ้น

ที่รัศมี 10 ขอบจะขยายออกนอกขอบรูปที่มองเห็นและเข้าไปในช่องว่าง ที่ 22 สีทองจะเติมช่องสี่เหลี่ยมของตัวอย่างนี้เต็ม รูปแบบ 18 4 ขยายตามแนวนอนมากกว่าแนวตั้ง การหดจะลดพื้นที่ทึบด้านนอกและขยายช่องโปร่งใส จุดแหลมด้านล่างก็เปลี่ยนไปด้วย
สิ่งที่สังเกตได้เหล่านี้ใช้กับภาพนี้และการตั้งค่าเหล่านี้เท่านั้น ไม่ใช่กฎเลือกรัศมีที่ปลอดภัยสำหรับโลโก้หรือไอคอนอื่นโดยทั่วไป
สร้างวงขอบอัลฟาด้านนอก
บันทึกตัวอย่างฉบับสมบูรณ์นี้เป็น outline.svg แล้วเปิดในเบราว์เซอร์ นี่คือ SVG รัศมี 10 ที่ใช้ในการจับภาพจริง:
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="220" viewBox="0 0 300 220">
<defs>
<g id="artwork">
<path fill="#173b40" fill-rule="evenodd"
d="M50 60H250V170H170L150 195L130 170H50Z M105 90H145V130H105Z"/>
<path fill="#f28567" d="M175 90H220V140H175Z"/>
</g>
<filter id="outline" filterUnits="userSpaceOnUse"
primitiveUnits="userSpaceOnUse" x="0" y="0" width="300" height="220">
<feMorphology in="SourceAlpha" operator="dilate" radius="10" result="grown"/>
<feComposite in="grown" in2="SourceAlpha" operator="out" result="ring"/>
<feFlood flood-color="#e4b84c" result="paint"/>
<feComposite in="paint" in2="ring" operator="in" result="coloredRing"/>
<feMerge>
<feMergeNode in="coloredRing"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<use href="#artwork" filter="url(#outline)"/>
</svg>ฟิลเตอร์ทำงานสี่ขั้นตอน:
feMorphologyขยายSourceAlphaเป็นgrownfeCompositeเก็บส่วนของgrownที่อยู่นอกอัลฟาต้นฉบับ ได้ผลเป็นringfeFloodให้สีทอง และการคอมโพสิตอีกครั้งจำกัดสีนี้ให้อยู่เฉพาะวงขอบfeMergeวางSourceGraphicต้นฉบับเหนือวงขอบที่เติมสีแล้ว
ลำดับข้อมูลขาเข้าสำคัญต่อการลบ ส่วนการคอมโพสิตของข้อกำหนด กำหนด in เป็นต้นทางและ in2 เป็นปลายทาง ในตัวอย่างนี้ in="grown", in2="SourceAlpha" และ operator="out" ลบพื้นที่ทึบเดิมออกจากอัลฟาที่ขยาย การสลับข้อมูลขาเข้าทั้งสองจะไม่ได้วงขอบด้านนอกแบบนี้
คำว่า ด้านนอก หมายถึงนอกบริเวณอัลฟาที่ทึบ รวมถึงพื้นที่ภายในช่องโปร่งใส หากขอบต้องหลีกเลี่ยงช่องเปิดด้านใน ฟิลเตอร์นี้ไม่ได้แยกสองกรณีนี้ให้โดยอัตโนมัติ ต้องมีรูปเงาแยกต่างหากหรือวิธีสร้างอื่นที่นิยามขอบด้านนอกตามที่ต้องการ
สีต้นทางยังมองเห็นได้เพราะเราผสาน SourceGraphic ที่ไม่แก้ไขเป็นขั้นสุดท้าย วิธีนี้แยกจากเส้นขอบพาธทั่วไป สำหรับเส้นขอบกึ่งกลาง เส้นขอบด้านในที่ใช้การตัด หรือเส้นขอบด้านนอกที่ใช้มาสก์ ดูการจัดแนวเส้นขอบ SVG ส่วนลำดับการระบายสี อธิบายกรณีซ้อนเส้นขอบกับสีเติมที่ง่ายกว่า
หดอัลฟาและเก็บสีในบริเวณที่เหลือ
แทนฟิลเตอร์ขอบด้วยนิยามต่อไปนี้ แล้วเปลี่ยน use สุดท้ายเป็น filter="url(#erode)":
<filter id="erode" filterUnits="userSpaceOnUse"
primitiveUnits="userSpaceOnUse" x="0" y="0" width="300" height="220">
<feMorphology in="SourceAlpha" operator="erode" radius="10" result="shrunken"/>
<feComposite in="SourceGraphic" in2="shrunken" operator="in"/>
</filter>การคอมโพสิตนี้เก็บภาพต้นฉบับเฉพาะบริเวณที่ยังมีอัลฟาหลังหด ไม่ได้ซ้อนต้นฉบับทั้งหมดกลับด้านบน เพราะจะบดบังผลการทำให้บางลงที่ต้องการ ในตัวอย่างนี้พื้นที่เขียวอมฟ้าด้านนอกเล็กลง ขณะที่บริเวณคอรัลภายในยังคงอยู่
ระบุ operator ให้ชัดเจนในตัวอย่างที่ใช้งาน เอกสารอ้างอิง operator แยก erode และ dilate บน feMorphology ออกจาก in และ out บน feComposite แอตทริบิวต์ชื่อเดียวกันมีหน้าที่ต่างกันในสององค์ประกอบนี้ ค่าเริ่มต้นของ operator สำหรับสัณฐานวิทยาคือ erode การละไว้จึงทำให้ได้ผลตรงข้ามกับการขยายที่ตั้งใจได้ง่าย
แยกหน่วยรัศมีออกจากบริเวณฟิลเตอร์
เอกสารอ้างอิง radius รองรับตัวเลขหนึ่งหรือสองค่า ค่าเดียวใช้กับทั้งสองแกน หากมีสองค่า ค่าแรกเป็นรัศมี x และค่าที่สองเป็นรัศมี y สำหรับรูปแบบขอบเพิ่มเติมสองแบบ ให้เปลี่ยนเฉพาะ radius="10" เป็น radius="22" หรือ radius="18 4" ในตัวอย่างฉบับสมบูรณ์
รัศมีเหล่านี้อยู่ในระบบพิกัดที่เลือกด้วย primitiveUnits ไม่ได้รับประกันว่าขอบจะหนาเท่าเดิมในหน่วยพิกเซล CSS ทุกขนาดแสดงผล เราใช้ primitiveUnits="userSpaceOnUse" อย่างชัดเจนกับ viewBox 0 0 300 220 การเปลี่ยนขนาด SVG ที่แสดงจะปรับสเกลเอฟเฟกต์ไปพร้อมภาพ
แอตทริบิวต์สองตัวที่ชื่อคล้ายกันควบคุมคนละเรื่อง:
| แอตทริบิวต์ | สิ่งที่ควบคุม | ค่าในตัวอย่าง |
|---|---|---|
| primitiveUnits | ค่าความยาวภายในคำสั่งฟิลเตอร์พื้นฐาน รวมถึงรัศมีสัณฐานวิทยา | userSpaceOnUse |
| filterUnits | x, y, width และ height ของบริเวณฟิลเตอร์ | userSpaceOnUse |
| viewBox ที่ราก | กรอบสี่เหลี่ยมในพิกัดผู้ใช้ที่แมปไปยังวิวพอร์ต SVG | 0 0 300 220 |
เอกสารอ้างอิง primitiveUnits ของ MDN อธิบายระบบพิกัดของคำสั่งฟิลเตอร์พื้นฐาน ส่วนเอกสารอ้างอิง filterUnits อธิบายบริเวณด้านนอกแยกต่างหาก filterUnits มีค่าเริ่มต้นเป็น objectBoundingBox และ primitiveUnits เป็น userSpaceOnUse การตั้งทั้งคู่ชัดเจนทำให้ตรวจตัวอย่างได้ง่ายขึ้น
ภาพต้นทางครอบคลุม x=50 ถึง 250 และ y=60 ถึง 195 รัศมีเท่ากันทั้งสองแกนที่ใหญ่ที่สุดในการทดสอบคือ 22 และบริเวณฟิลเตอร์กับวิวพอร์ตขนาด 300 คูณ 220 ยังมีพื้นที่รองรับผลลัพธ์นั้น ตรวจขอบเขตทั้งสองอีกครั้งเมื่อเปลี่ยนภาพหรือรัศมี ข้อกำหนดอธิบายบริเวณฟิลเตอร์ว่าเป็นขอบตัดแบบเด็ดขาด รัศมีที่ใหญ่ขึ้นไม่สามารถระบายเลยบริเวณนี้ได้ คู่มือการตัดด้วยฟิลเตอร์และวิวพอร์ต SVG สาธิตข้อจำกัดสองอย่างนี้แยกกัน
หากฟิลเตอร์ดูเหมือนไม่ทำงาน ให้ตรวจ radius ด้วย ค่าเริ่มต้นคือ 0 และเอกสารอ้างอิงระบุว่าค่าติดลบหรือศูนย์จะปิดผลของคำสั่งฟิลเตอร์พื้นฐาน ตัวอย่างข้างต้นใช้ค่าบวกอย่างชัดเจนทั้งสองแกน
ตรวจช่องว่างและมุมก่อนส่งมอบ
บริเวณใกล้เคียงรูปสี่เหลี่ยมผืนผ้าของสัณฐานวิทยาทำให้รูปลักษณ์ต่างจากขอบมนทั่วไป รัศมี x และ y ที่เท่ากันไม่ได้ทำให้บริเวณนั้นเป็นวงกลม จุดแหลมด้านล่างที่กลายเป็นขั้นในภาพเปรียบเทียบเป็นตำแหน่งที่เหมาะสำหรับตรวจความแตกต่างนี้
| ผลลัพธ์ | สิ่งที่ควรตรวจก่อนใช้ |
|---|---|
| ขอบด้านนอกใหญ่ขึ้น | ช่องเปิดและรูปทรงใกล้กันอาจดูเชื่อมติดกัน |
| รูปเงาที่หดลง | รายละเอียดบางอาจหายไปและช่องว่างอาจใหญ่ขึ้น |
| รัศมีแต่ละแกนไม่เท่ากัน | การขยายแนวนอนและแนวตั้งต่างกันโดยตั้งใจ |
| ภาพตัวอย่างในเบราว์เซอร์ถูกต้อง | ยังต้องทดสอบในโปรแกรมแก้ไขหรือตัวส่งออกปลายทาง |
| ต้องการขอบเป็นเรขาคณิตที่แก้ไขได้ | ผลของฟิลเตอร์ขณะเรนเดอร์ไม่ใช่เส้นรอบรูปใหม่ในต้นทาง |
เราตรวจว่าทั้งห้าไฟล์ยังมีพาธต้นทางสองพาธที่แอตทริบิวต์ d เหมือนกัน นิยามฟิลเตอร์ต่างกัน แต่เรขาคณิตต้นทางไม่ต่าง ตรวจวัตถุ SVG จริงเมื่อปลายทางต้องการพาธที่แก้ไข ออฟเซ็ต หรือส่งไปผลิตได้ ใช้คำสั่งจัดการเรขาคณิตของโปรแกรมแก้ไขสำหรับความต้องการนั้น แล้วตรวจเส้นรอบรูปที่ได้ การเปลี่ยนหรือลบฟิลเตอร์นี้เพียงอย่างเดียวไม่ได้สร้างเส้นเหล่านั้น
เก็บไฟล์ต้นฉบับที่ไม่มีฟิลเตอร์และสำเนาสำหรับส่งมอบแยกกัน เปิดไฟล์ที่ส่งมอบในปลายทางจริง แล้วเปรียบเทียบมุม ช่องเปิดภายใน สี และขอบเขต ตัวอย่างในเบราว์เซอร์นี้ไม่ได้ยืนยันการรองรับในแอปออกแบบ ตัวส่งออก หรือเครื่องตัดใดโดยเฉพาะ
เตรียมต้นทางก่อนเพิ่มเอฟเฟกต์
PerfectVector ช่วยได้เมื่อภาพเริ่มต้นมีเพียง PNG หรือ JPG เตรียม SVG สำหรับประเมิน แล้วตรวจรูปเงาต้นทาง ช่องเล็ก ๆ และขอบเขตสีที่สร้างกลับขึ้นมาก่อนใช้ฟิลเตอร์ ขอบจากสัณฐานวิทยาจะขยายปัญหาในรูปเงานั้น ไม่ได้ซ่อมเรขาคณิตให้ตรงตามที่ตั้งใจแทนคุณ
ใช้ไฟล์เวกเตอร์ต้นฉบับโดยตรงหากมี สำหรับสัญลักษณ์ง่าย ๆ การวาดใหม่ด้วยมืออาจควบคุมได้สะดวกกว่า การไล่เส้นภาพที่รวมเลเยอร์และฝังขอบไว้แล้วไม่สามารถกู้การตั้งค่าฟิลเตอร์ต้นฉบับ หรือบอกได้ว่าส่วนใดคือรูปร่างก่อนใช้ฟิลเตอร์ ภาพรวมการแปลงภาพเป็นเวกเตอร์ อธิบายข้อจำกัดของการสร้างกลับขึ้นมานี้
คำถามที่พบบ่อย
feMorphology สร้างพาธขอบที่แก้ไขได้หรือไม่? ไม่ ฟิลเตอร์นี้เปลี่ยนผลลัพธ์ที่เรนเดอร์ ตัวอย่างคงพาธต้นทางสองพาธเดิมไว้ ดังนั้นปลายทางที่ต้องการเส้นรอบรูปแยกต้องใช้คำสั่งจัดการเรขาคณิตและตรวจการนำเข้า
ทำไมขอบสีทองจึงเติมช่องว่างใน SVG ของฉัน? วงขอบขยายออกนอกอัลฟาที่ทึบ รวมถึงเข้าไปในช่องโปร่งใส ในตัวอย่างนี้ รัศมี 22 เติมช่องสี่เหลี่ยมเต็ม ภาพอื่นต้องตรวจระยะช่องว่างและรัศมีของตนเอง
ทำไมใช้ SourceAlpha แทน SourceGraphic? SourceAlpha ทำให้ฟิลเตอร์ทำงานกับรูปเงาโดยไม่ขยายแต่ละช่องสีต้นฉบับ ตัวอย่างเติมสีให้วงขอบอัลฟาแยกต่างหาก แล้วผสาน SourceGraphic เป็นขั้นสุดท้ายเพื่อแสดงสีเดิม
แหล่งอ้างอิง
- W3C — Filter Effects ระดับ 1 — นิยามบริเวณใกล้เคียงรูปสี่เหลี่ยมผืนผ้าของสัณฐานวิทยา ข้อมูลขาเข้าการคอมโพสิต และการตัดที่บริเวณฟิลเตอร์
- MDN — radius — นิยามไวยากรณ์รัศมีค่าเดียวและสองค่า หน่วย และค่าที่ปิดเอฟเฟกต์
- MDN — operator — แยกการดำเนินการสัณฐานวิทยาออกจากการคอมโพสิต
- MDN — primitiveUnits — กำหนดระบบพิกัดสำหรับรัศมีสัณฐานวิทยา
- MDN — filterUnits — กำหนดระบบพิกัดสำหรับบริเวณฟิลเตอร์ด้านนอก
หากต้นทางมีเพียงราสเตอร์ เตรียมภาพเวกเตอร์ด้วย PerfectVector ตรวจรูปเงาและช่องเปิดก่อนใช้ฟิลเตอร์ จากนั้นเพิ่มขอบและเปรียบเทียบไฟล์สุดท้ายในโปรแกรมดูปลายทาง
เพิ่มเติมจากบล็อก

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

ภาพ SVG ใน Graphviz: ย้ายภาพในโหนดได้ครบ
เพิ่มภาพ SVG ในโหนด Graphviz ตรวจการอ้างอิงภาพ และทดสอบชุดแอสเซ็ตครบถ้วนเทียบกับแผนภาพที่ย้ายเดี่ยวและสำเนาส่งมอบที่มีข้อมูลครบในตัว