PerfectVector
โดย Irene Kimอ่าน 4 นาที

คำสั่งพาธ SVG แบบสัมพัทธ์: ติดตามจุดปัจจุบัน

อ่านพาธ SVG แบบสัมบูรณ์และสัมพัทธ์ด้วยการติดตามจุดปัจจุบัน ทดสอบคู่พิกัด moveto การรีเซ็ตจาก closepath และพิกัดเส้นโค้งคิวบิกด้วยตัวอย่างต้นฉบับที่ให้ผลตรงกัน

ในหน้านี้

คำสั่งพาธ SVG แบบสัมบูรณ์ เช่น M, L และ C ใช้ตำแหน่งในระบบพิกัดผู้ใช้ปัจจุบัน ส่วนรูปแบบสัมพัทธ์อย่าง m, l และ c ใช้ระยะเยื้องจากจุดปัจจุบัน เมื่ออ่านหรือเขียนพาธใหม่ ให้ติดตามจุดนี้หลังจบแต่ละช่วง การเปลี่ยนตัวอักษรคำสั่งโดยไม่เปลี่ยนตัวเลขอาจทำให้รูปร่างเปลี่ยนไป

กฎสองข้อที่มักทำให้สับสนคือ คู่พิกัดแรกของ m ที่เป็นคำสั่งเริ่มต้นจะทำหน้าที่เป็นการย้ายแบบสัมบูรณ์ ส่วนคู่ถัดไปจะเป็นเส้นตรงแบบสัมพัทธ์ และ Z หรือ z จะปิดพาธย่อยพร้อมรีเซ็ตจุดปัจจุบันกลับไปที่จุดเริ่มต้นของพาธย่อยนั้น การย้ายแบบสัมพัทธ์ครั้งใหม่หลังการปิดจะอ้างอิงจุดที่รีเซ็ตแล้ว

แยกอ่านตำแหน่งกับระยะเยื้อง

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

เมื่อเริ่มที่ (40, 40) คำสั่งสองแบบนี้อธิบายเส้นเดียวกัน:

L180 40
l140 0

แบบแรกระบุจุดปลาย (180, 40) ส่วนแบบที่สองบวก (140, 0) เข้ากับ (40, 40) ทั้งคู่จบที่ (180, 40) ซึ่งกลายเป็นจุดปัจจุบันสำหรับช่วงถัดไป

แต่ l180 40 จะบวก (180, 40) และจบที่ (220, 80) คำสั่งนี้มีไวยากรณ์ถูกต้อง แต่วาดเส้นคนละเส้น

คำสั่งความหมายของค่าตำแหน่ง
M x yเริ่มพาธย่อยที่ตำแหน่งที่ระบุ
m dx dyเริ่มพาธย่อยโดยอ้างอิงจุดปัจจุบัน โดยมีพฤติกรรมของ m เริ่มต้นตามที่อธิบายด้านล่าง
L x yวาดเส้นไปยังตำแหน่งหนึ่ง
l dx dyวาดเส้นไปยังจุดปัจจุบันบวกระยะเยื้อง
C / cระบุจุดควบคุมสองจุดและจุดปลาย ด้วยตำแหน่งหรือระยะเยื้องจากจุดเริ่มต้นเดียวกัน
Z / zปิดกลับไปยังจุดเริ่มต้นของพาธย่อยและรีเซ็ตจุดปัจจุบัน

Z ตัวพิมพ์ใหญ่และตัวพิมพ์เล็กให้ผลเหมือนกัน ทั้งคู่ไม่มีอาร์กิวเมนต์พิกัด เอกสารอ้างอิง d ของ MDN แสดงรายการคำสั่งและพารามิเตอร์ของแต่ละคำสั่ง

เปรียบเทียบคำอธิบายสองแบบของภาพต้นฉบับเดียวกัน

บันทึก SVG ฉบับสมบูรณ์นี้เป็น absolute.svg แล้วเปิดในเบราว์เซอร์ ภายในมีตราสีเขียวอมฟ้าที่ประกอบจากหลายพาธย่อย มีช่องสี่เหลี่ยมจัตุรัส และมีเส้นโค้งคิวบิกสีคอรัลแยกอีกหนึ่งเส้น:

<svg xmlns="http://www.w3.org/2000/svg" width="300" height="240" viewBox="0 0 300 240">
  <path class="badge" fill="#173b40" fill-rule="evenodd" d="M40 40L180 40L210 80L180 140L40 140Z M80 70L80 110L120 110L120 70Z"/>
  <path class="curve" fill="none" stroke="#f28567" stroke-width="6" d="M40 190C80 145 155 225 220 180"/>
</svg>

สำหรับ relative.svg ให้คงขนาด SVG, viewBox และสไตล์ไว้ แต่แทนที่องค์ประกอบพาธทั้งสองด้วยข้อมูลนี้:

<path class="badge" fill="#173b40" fill-rule="evenodd"
  d="m40 40 140 0 30 40 -30 60 -140 0z m40 30 0 40 40 0 0 -40z"/>
<path class="curve" fill="none" stroke="#f28567" stroke-width="6"
  d="m40 190c40 -45 115 35 180 -10"/>

เราเรนเดอร์ไฟล์ต้นฉบับทั้งสองในเบราว์เซอร์ พร้อมสำเนาตราแบบสัมบูรณ์ที่จงใจแก้ให้ผิด สำเนาที่ผิดเปลี่ยนเฉพาะ L180 40 ตัวแรกเป็น l180 40 โดยคงตัวเลขเดิม

ภาพเรนเดอร์ SVG จริงแสดงตราที่ประกอบจากหลายพาธย่อยและเส้นโค้งคิวบิกแบบสัมบูรณ์กับสัมพัทธ์ที่ตรงกัน พร้อมตราที่บิดเบี้ยวหลังเปลี่ยนตัวพิมพ์ของคำสั่งเส้นหนึ่งโดยไม่แปลงพิกัด
คำอธิบายสองแบบแรกให้ผลตรงกัน แบบที่สามเปลี่ยนจุดปลายของเส้นแรกจาก (180, 40) เป็น (220, 80) แผงด้านล่างติดตามการปิดพาธ ระยะเยื้องของเส้นคิวบิก และค่าที่เบราว์เซอร์วัดจากคู่ที่ทดสอบ

หน้าทดสอบของเราใช้ getTotalLength() และ getPointAtLength() ของ SVG ที่เบราว์เซอร์มีให้ เพื่อเปรียบเทียบตัวอย่าง 301 จุดต่อพาธที่สัดส่วนความยาวรวมตรงกัน ค่าความยาวที่แสดงคือ 657.082 หน่วยผู้ใช้สำหรับตราแต่ละแบบ และ 189.066 สำหรับเส้นโค้งแต่ละแบบ ความแตกต่างสูงสุดของจุดที่เก็บตัวอย่างมาวัดแสดงเป็น 0.000000 สำหรับทั้งสองคู่

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

ติดตาม m ตัวแรกและเส้นตรงโดยนัย

ตราของเราเริ่มด้วย:

m40 40 140 0 30 40 -30 60 -140 0z

มีเพียงคู่แรกที่ทำหน้าที่เป็นการย้าย คู่ที่เหลือทำงานเป็นช่วง l แบบสัมพัทธ์โดยนัย นิยาม moveto ระบุชัดว่าคู่ถัดไปยังคงเป็นแบบสัมพัทธ์ แม้ m40 40 เริ่มต้นจะถือเป็นตำแหน่งเริ่มต้นแบบสัมบูรณ์ก็ตาม

ติดตามทีละคู่:

คู่พิกัดหรือคำสั่งการทำงานจุดปัจจุบันหลังทำงาน
m40 40การย้ายเริ่มต้น(40, 40)
140 0เส้นตรงสัมพัทธ์โดยนัย(180, 40)
30 40เส้นตรงสัมพัทธ์โดยนัย(210, 80)
-30 60เส้นตรงสัมพัทธ์โดยนัย(180, 140)
-140 0เส้นตรงสัมพัทธ์โดยนัย(40, 140)
zปิดกลับไปยังจุดเริ่มต้นของพาธย่อยนี้(40, 40)

เมื่อเทียบกัน M ตัวพิมพ์ใหญ่ที่มีหลายคู่จะเปลี่ยนคู่ถัดไปให้เป็นช่วง L แบบสัมบูรณ์โดยนัย ไม่ได้ตีความทุกคู่เป็นการย้ายครั้งใหม่ และไม่ได้ใช้ผลต่างพิกัดแบบเดียวกับตัวอย่างตัวพิมพ์เล็กนี้

องค์ประกอบพาธแต่ละตัวมีลำดับพาธของตนเอง อย่าเริ่ม m แบบสัมพัทธ์ตัวแรกจากจุดปลายขององค์ประกอบพาธก่อนหน้าที่ไม่เกี่ยวข้องกัน

ใช้จุดเริ่มต้นของพาธย่อยที่ปิดแล้วสำหรับการย้ายครั้งถัดไป

ช่องว่างเริ่มหลัง z ตัวแรก:

m40 30 0 40 40 0 0 -40z

ขณะนั้นจุดปัจจุบันคือ (40, 40) การย้ายสัมพัทธ์ครั้งใหม่จึงบวก (40, 30) และเริ่มช่องว่างที่ (80, 70) จากนั้นเส้นตรงโดยนัยไปถึง (80, 110), (120, 110) และ (120, 70) ก่อนปิด

หากเผลอใช้จุดปลายเส้นสุดท้ายของตราด้านนอก (40, 140) เป็นฐาน คุณจะวางการย้ายนี้ที่ (80, 170) การปิดพาธก่อนหน้านี้คือเหตุผลที่ทำให้การคำนวณนั้นผิด นิยาม closepath รีเซ็ตจุดปัจจุบันเป็นจุดเริ่มต้นของพาธย่อยปัจจุบัน

พาธย่อยทั้งสองอยู่ในองค์ประกอบพาธเดียวกัน fill-rule="evenodd" ทำให้สี่เหลี่ยมด้านในเป็นช่องเปิดในตัวอย่างทดสอบนี้ คงกฎการเติมสีและการตั้งค่าการระบายสีไว้เหมือนเดิมเมื่อเปรียบเทียบการเขียนพิกัดใหม่ เพื่อไม่ให้การเปลี่ยนการเติมสีบดบังเรขาคณิตที่กำลังทดสอบ

วัดจุดควบคุมคิวบิกแต่ละจุดจากจุดเริ่มต้นเดียวกัน

เส้นโค้งที่แยกออกมาเริ่มที่ (40, 190):

M40 190 C80 145 155 225 220 180
m40 190 c40 -45 115 35 180 -10

สำหรับช่วง c ตัวพิมพ์เล็กช่วงนี้ คู่พิกัดทั้งสามเป็นระยะเยื้องจาก (40, 190) จุดควบคุมที่สองไม่ได้วัดจากจุดควบคุมแรก และจุดปลายก็ไม่ได้วัดจากจุดควบคุมที่สอง

จุดคู่พิกัดสัมพัทธ์ตำแหน่งสัมบูรณ์
จุดควบคุมแรก(40, -45)(80, 145)
จุดควบคุมที่สอง(115, 35)(155, 225)
จุดปลาย(180, -10)(220, 180)

เมื่อจบช่วง ให้ปรับจุดปัจจุบันเป็นจุดปลาย (220, 180) ช่วงสัมพัทธ์ถัดไปจะใช้ตำแหน่งเริ่มต้นใหม่นี้ บทสอนพาธของ MDN อธิบายจุดควบคุมคิวบิกและรูปแบบสัมบูรณ์/สัมพัทธ์ ส่วนคู่มือรอยต่อเบซิเยร์ อธิบายแขนควบคุมแนวสัมผัสและคำสั่งย่อ S แยกต่างหาก

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

ย้ายภาพโดยไม่สับสนระหว่างการจัดวางกับไวยากรณ์พาธ

สำหรับตราประกอบที่ใช้พิกัดสัมพัทธ์ทั้งหมดนี้ การเปลี่ยน m40 40 เริ่มต้นเป็น m60 60 จะเลื่อนจุดยอดไป (20, 20) เพราะผลต่างพิกัดถัดไปและการย้ายพาธย่อยสัมพัทธ์จะอิงจุดเริ่มต้นที่เปลี่ยนแล้ว ส่วนเส้นโค้งอีกพาธหนึ่งจะไม่เปลี่ยน ในข้อมูลที่ผสมสองรูปแบบ ตำแหน่งสัมบูรณ์ภายหลังก็ยังยึดกับพิกัดที่ระบุไว้ การแก้การย้ายครั้งแรกจึงอาจทำให้พาธบิดเบี้ยวได้

หากต้องการย้ายภาพทั้งสองพาธเป็นหน่วยเดียว ให้คงสตริง d ทั้งคู่ไว้ แล้ววางองค์ประกอบไว้ในกลุ่มที่มี transform="translate(20 20)" ข้อกำหนดระบบพิกัด SVG อธิบายว่าการแปลงสร้างการแมปพิกัดอย่างไร การแปลงที่กลุ่มจะเปลี่ยนการจัดวางโดยไม่เขียนข้อมูลพาธต้นฉบับใหม่

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

ตรวจเรขาคณิตต้นทางก่อนเปลี่ยนรูปแบบคำสั่ง

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

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

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

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

m ตัวพิมพ์เล็กตัวแรกอ้างอิงพาธอื่นหรือไม่? ไม่ เมื่อ m เป็นคำสั่งแรกของพาธ คู่พิกัดแรกจะถือเป็นตำแหน่งเริ่มต้นแบบสัมบูรณ์ คู่เพิ่มเติมเป็นเส้นสัมพัทธ์โดยนัย และจุดปลายขององค์ประกอบพาธอื่นไม่ได้เป็นฐานเริ่มต้นให้มัน

จุดปัจจุบันอยู่ที่ไหนหลัง Z หรือ z? อยู่ที่จุดเริ่มต้นของพาธย่อยที่เพิ่งปิด การย้ายสัมพัทธ์ครั้งถัดไปใช้จุดที่รีเซ็ตแล้วนี้ ไม่ใช่จุดปลายของเส้นก่อนหน้า

แปลงพาธด้วยการเปลี่ยนคำสั่งเป็นตัวพิมพ์เล็กได้หรือไม่? ไม่ได้ ต้องคำนวณค่าตำแหน่งใหม่ตามความหมายของแต่ละคำสั่ง L180 40 ในตัวอย่างนี้กลายเป็น l140 0 เมื่อจุดปัจจุบันคือ (40, 40) ส่วน l180 40 จะวาดเส้นที่ต่างออกไป

แหล่งอ้างอิง

  1. W3C — พาธใน SVG 2 — นิยามจุดปัจจุบัน moveto เริ่มต้น เส้นตรงโดยนัย การปิดพาธ และพารามิเตอร์คิวบิก
  2. MDN — บทสอนพาธ — แสดงตัวอย่างคำสั่งเส้นตรงและเส้นโค้งแบบสัมบูรณ์กับสัมพัทธ์
  3. MDN — แอตทริบิวต์ d — แสดงหน้าที่ของพารามิเตอร์คำสั่ง รวมถึงส่วนโค้งและ closepath
  4. W3C — ระบบพิกัดใน SVG 2 — อธิบายการแมปพิกัดผู้ใช้และการจัดวางภาพด้วยการแปลงพิกัด

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

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

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