การวาดวงกลมด้วยเส้นทางอาร์คของ SVG


147

คำถามสั้น ๆ : การใช้เส้นทาง SVG เราสามารถวาด 99.99% ของวงกลมและปรากฏขึ้น แต่เมื่อเป็น 99.99999999% ของวงกลมแล้ววงกลมจะไม่ปรากฏขึ้น จะแก้ไขอย่างไร?

เส้นทาง SVG ต่อไปนี้สามารถวาดวงกลมได้ 99.99%: (ลองดูที่http://jsfiddle.net/DFhUF/1381/และดูว่าคุณเห็น 4 arcs หรือ 2 arcs เพียง 2 แต่โปรดทราบว่าถ้าเป็น IE มันจะเป็น แสดงผลใน VML ไม่ใช่ SVG แต่มีปัญหาที่คล้ายกัน)

M 100 100 a 50 50 0 1 0 0.00001 0

แต่เมื่อมันเป็น 99.99999999% ของวงกลมแล้วไม่มีอะไรจะแสดงเลย?

M 100 100 a 50 50 0 1 0 0.00000001 0    

และนั่นก็เท่ากับ 100% ของวงกลม (มันยังคงเป็นส่วนโค้งไม่ใช่หรือเป็นเพียงส่วนโค้งที่สมบูรณ์มาก)

M 100 100 a 50 50 0 1 0 0 0 

จะแก้ไขได้อย่างไร? เหตุผลก็คือฉันใช้ฟังก์ชั่นเพื่อวาดเปอร์เซ็นต์ของส่วนโค้งและถ้าฉันต้องการ "กรณีพิเศษ" ส่วนโค้ง 99.9999% หรือ 100% เพื่อใช้ฟังก์ชั่นวงกลมนั่นจะเป็นเรื่องโง่

อีกกรณีทดสอบบน jsfiddle โดยใช้ RaphaelJS อยู่ที่http://jsfiddle.net/DFhUF/1381/
(และถ้าเป็น VML บน IE 8 แม้แต่วงที่สองจะไม่แสดง ... คุณต้องเปลี่ยนเป็น 0.01)


ปรับปรุง:

นี่เป็นเพราะฉันแสดงส่วนโค้งสำหรับคะแนนในระบบของเราดังนั้น 3.3 คะแนนจะได้ 1/3 ของวงกลม 0.5 รับครึ่งวงกลมและ 9.9 คะแนนรับ 99% ของวงกลม แต่จะเกิดอะไรขึ้นถ้ามีคะแนนที่เป็น 9.99 ในระบบของเรา ฉันต้องตรวจสอบว่ามันอยู่ใกล้กับ 99.999% ของวงกลมหรือไม่และใช้arcฟังก์ชั่นหรือcircleฟังก์ชั่นตามลำดับหรือไม่? ถ้าอย่างนั้นคะแนน 9.9987 ล่ะ จะใช้อันไหนดี? มันไร้สาระที่จะต้องรู้ว่าคะแนนประเภทใดจะจับคู่กับ "วงกลมที่สมบูรณ์เกินไป" และเปลี่ยนเป็นฟังก์ชันวงกลมและเมื่อเป็น "วงกลมบางอัน 99.9%" ของวงกลมหรือคะแนน 9.9987 จากนั้นใช้ฟังก์ชันอาร์ค .


@ มินิเทคแน่นอนว่ามันใช้งานได้ ... เป็น 99% ของแวดวง กรณีนี้คือมันสามารถวาด 98%, 99%, 99.99% ของวงกลม แต่ไม่ใช่ 99.9999999% หรือ 100%
nonopolarity

1
ลิงก์ทั้งสองไปยังสิ่งเดียวกันและใช้งานได้ดีใน Safari
Mark Bessey

ถูกต้องลิงก์เดียวกันฉันแค่ต้องการให้คนเห็นกรณีทดสอบก่อนหน้านี้ดังนั้นฉันจึงเพิ่มลิงก์ที่จุดเริ่มต้นของคำถาม ซาฟารีที่เหมาะสมจะทำมันอย่างไรดี ... Chrome และ Firefox จะไม่ ... ชนิดของ coz Safari และ Chrome ที่แปลกคือ Webkit ... แต่เครื่องมือ SVG ขึ้นอยู่กับ Webkit หรือไม่
nonopolarity

@Marcin ดูดีอย่างไร คุณเห็น 4 โค้งหรือ 2 โค้งหรือไม่ คุณดูรหัสหรือยัง
nonopolarity

2
jsfiddle ที่ได้รับการอัพเดตพร้อม Raphael รวมอยู่ในไลบรารีเพื่อรับข้อผิดพลาดระหว่างการโหลด raphael.js: jsfiddle.net/DFhUF/1381
ericsoco

คำตอบ:


35

เช่นเดียวกับส่วนโค้งของ XAML เพียงปิดส่วนโค้ง 99.99% ด้วย a Zและคุณจะได้วงกลม!


คุณสามารถปิดกรณีที่สามในตัวอย่าง jsfiddle และทำให้มันทำงานได้หรือไม่?
nonopolarity

โดยลดค่าเป็น 0.0001 ใช่ ปัญหาเสียงที่เกี่ยวข้องกับการใช้งาน WebKit ของเบราว์เซอร์ที่หลากหลายไม่ใช่ SVG เอง แต่นั่นคือวิธีที่คุณสร้างวงกลมในส่วนประกอบ SVG / XAMLs Arc
ทอดด์เมน

Ahhh การโกงทางออกที่ดีที่สุดเสมอ ยังคงต้องจัดการกับเคส 100% แต่เพียงแค่ "ปัดเศษลง" เป็น 99.999% แทนที่จะใช้รหัสสาขาแยกทั้งหมด
SimonR

การสาธิตใด ๆ ? คำตอบนี้ไม่ตอบสนอง
Medet Tleukabiluly

@MedetTleukabiluly คุณมีส่วนโค้งด้านบนในคำถามเพิ่มzที่สิ้นสุดและทำ คุณอาจต้องลบเลขศูนย์ออกตัวอย่างเช่นใน Chrome ล่าสุดฉันต้องเขียน0.0001เพื่อให้มันใช้งานได้ หากคุณกำลังมองหาสถานที่ที่จะวางสตริงเส้นทางดูเส้นทางบน MDN
TWiStErRob

416

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

<path 
    d="
    M cx cy
    m -r, 0
    a r,r 0 1,0 (r * 2),0
    a r,r 0 1,0 -(r * 2),0
    "
/>

ในคำอื่น ๆ นี้:

<circle cx="100" cy="100" r="75" />

สามารถบรรลุเป็นเส้นทางกับสิ่งนี้:

  <path 
        d="
        M 100, 100
        m -75, 0
        a 75,75 0 1,0 150,0
        a 75,75 0 1,0 -150,0
        "
  />

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

เหตุผลที่มันไม่สามารถทำได้ครบวงจรในหนึ่งส่วนโค้ง (และฉันแค่คาดเดา) เพราะคุณจะบอกให้วาดส่วนโค้งจากตัวเอง (สมมติว่า 150,150) กับตัวเอง (150,150) ซึ่งทำให้ ในฐานะ "โอ้ฉันมีอยู่แล้วไม่จำเป็นต้องมีส่วนโค้ง!"

ประโยชน์ของโซลูชันที่ฉันเสนอคือ:

  1. มันง่ายในการแปลจากวงกลมไปยังเส้นทางโดยตรงและ
  2. ไม่มีการทับซ้อนกันในสองเส้นโค้ง (ซึ่งอาจทำให้เกิดปัญหาหากคุณใช้เครื่องหมายหรือรูปแบบ ฯลฯ ) มันเป็นเส้นต่อเนื่องที่สะอาดแม้จะวาดเป็นสองชิ้น

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

การสาธิต jsfiddle: http://jsfiddle.net/crazytonyi/mNt2g/

ปรับปรุง:

หากคุณใช้เส้นทางสำหรับการtextPathอ้างอิงและคุณต้องการให้ข้อความแสดงผลที่ขอบด้านนอกของส่วนโค้งคุณจะต้องใช้วิธีการเดียวกัน แต่เปลี่ยนค่าสถานะการกวาดจาก 0 เป็น 1 เพื่อให้ปฏิบัติกับด้านนอกของ เส้นทางเป็นพื้นผิวแทนด้านใน (คิดว่า1,0เป็นคนนั่งอยู่ตรงกลางและวาดวงกลมรอบตัวเองในขณะ1,1ที่บางคนกำลังเดินไปรอบ ๆ ศูนย์ในระยะรัศมีและลากชอล์กอยู่ข้างๆหากมีความช่วยเหลือ) นี่คือรหัสข้างต้น แต่มีการเปลี่ยนแปลง:

<path 
    d="
    M cx cy
    m -r, 0
    a r,r 0 1,1 (r * 2),0
    a r,r 0 1,1 -(r * 2),0
    "
/>

1
+1, ขอบคุณสำหรับสูตร แน่นอนสองคำสั่งแรกสามารถรวมกันได้
John Gietzen

+1 สำหรับความชัดเจนและการปฏิบัติจริงของการแก้ปัญหาโดยทั่วไป แต่โดยเฉพาะอย่างยิ่งสำหรับ'องค์ประกอบรูปร่างเช่นวงกลมไม่ได้มีจุดเริ่มต้นในทางเทคนิค - เป็นวิธีที่ชัดเจนในการแสดงปัญหา
เดวิดจอห์นเวลส์

11
ฉันคิดว่าปัญหาที่เกิดขึ้นไม่ใช่ว่า "โอ้ฉันอยู่ที่นั่นแล้วไม่จำเป็นต้องมีส่วนโค้ง!" โดยให้ 1 เป็นธงอาร์คขนาดใหญ่ที่คุณบอกเครื่องยนต์อย่างชัดเจนว่าคุณต้องการให้มันไปอีกทาง ปัญหาที่แท้จริงคือมีวงจำนวนมากที่ จำกัด ข้อ จำกัด ของการผ่านจุดของคุณ สิ่งนี้อธิบายว่าทำไมเมื่อคุณต้องการส่วนโค้ง 360 * ดังนั้นเครื่องยนต์จึงไม่รู้ว่าต้องทำอย่างไร เหตุผลที่มันใช้ไม่ได้กับ 359.999 ก็คือนี่เป็นการคำนวณที่ไม่เสถียรเชิงตัวเลขและในขณะที่ในทางเทคนิคมีวงกลมหนึ่งวงที่ตรงกับคำขอมันอาจจะไม่ใช่สิ่งที่คุณต้องการอยู่ดี
qbolec

@qbolec - ถูกต้องฉันคิดในแง่ของการโค้งขนาดใหญ่และการกวาดออกและส่วนโค้งไม่มีปลายทาง หรืออย่างน้อยที่สุดถึงแม้จะมีส่วนโค้งขนาดใหญ่และกวาดเปิดใช้งานที่มีการออกแบบพื้นฐานบางอย่างที่กำหนดส่วนโค้งเป็นเส้นโค้งระหว่างสองจุด (เพื่อให้มีจุดหนึ่งที่ใช้สองครั้งก็เห็นว่ามันเป็นจุดเดียวยุบ) การมองเห็นของส่วนโค้งอาจจะวาดวงกลม 360 รอบจุดหนึ่งได้อย่างสมเหตุสมผลแม้ว่ามันจะยุบลงไปเป็นวงกลมหนึ่งวงถ้ามีการกำหนดจุดศูนย์กลางซึ่งทำให้เกิดคำถามที่ว่าอาร์คเดี่ยวสามารถทำให้เต็มวงได้หรือไม่?
แอนโธนี

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

17

ในการอ้างอิงถึงวิธีการแก้ปัญหาของแอนโทนี่นี่คือฟังก์ชั่นที่จะได้รับเส้นทาง:

function circlePath(cx, cy, r){
    return 'M '+cx+' '+cy+' m -'+r+', 0 a '+r+','+r+' 0 1,0 '+(r*2)+',0 a '+r+','+r+' 0 1,0 -'+(r*2)+',0';
}

10

แนวทางที่แตกต่างอย่างสิ้นเชิง:

แทนที่จะเล่นซอกับพา ธ เพื่อระบุส่วนโค้งใน svg คุณสามารถใช้องค์ประกอบวงกลมและระบุเส้นขีด -dasharray ในโค้ดหลอก:

with $score between 0..1, and pi = 3.141592653589793238

$length = $score * 2 * pi * $r
$max = 7 * $r  (i.e. well above 2*pi*r)

<circle r="$r" stroke-dasharray="$length $max" />

ความเรียบง่ายของมันเป็นข้อได้เปรียบหลักของวิธีการหลายอาร์คพา ธ (เช่นเมื่อสคริปต์คุณเพียงแค่เสียบค่าเดียว

ส่วนโค้งเริ่มต้นที่จุดขวาสุดและสามารถเลื่อนไปรอบ ๆ โดยใช้การแปลงแบบหมุน

หมายเหตุ: Firefox มีข้อผิดพลาดแปลก ๆ ที่มีการหมุนมากกว่า 90 องศาหรือมากกว่านั้น ดังนั้นในการเริ่มต้นส่วนโค้งจากด้านบนใช้:

<circle r="$r" transform="rotate(-89.9)" stroke-dasharray="$length $max" />

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

@mxfh ไม่จริงถ้าคุณใช้ความกว้างของจังหวะสองเท่าของค่า r คุณจะได้ภาคที่สมบูรณ์แบบ
mvds

ด้วยความstroke-dasharray="0 10cm 5cm 1000cm"เป็นไปได้ที่จะหลีกเลี่ยงการเปลี่ยนแปลง
stenci

@stenci ใช่ แต่ข้อเสียคือคุณไม่สามารถมีหนึ่งพารามิเตอร์ใน dasharray เพื่อปรับขนาดจาก 0% ถึง 100% เติม (ซึ่งเป็นหนึ่งในวัตถุประสงค์ของฉัน)
mvds

5

Adobe Illustrator ใช้เส้นโค้งเบซิเยร์เช่น SVG และสำหรับวงกลมมันจะสร้างสี่จุด คุณสามารถสร้างวงกลมที่มีสองคำสั่งวงรีรูปไข่ ... แต่แล้วสำหรับวงกลมใน SVG ฉันจะใช้<circle />:)


"คุณสามารถสร้างวงกลมด้วยสองคำสั่งรูปวงรี" คุณหมายถึงอะไร คุณใช้ไม่ได้เหรอ อย่างน้อยก็ทำไปจาก (0,0) ถึง (0.01, 0) เพื่อให้มันแสดงผลบางอย่าง สำหรับการใช้circleงานโปรดดูการปรับปรุงในคำถามแทน
nonopolarity

ไม่ฉันไม่คิดว่าคุณจะใช้เพียงอันเดียว คุณแสดงให้เห็นว่าคุณทำไม่ได้และคุณมีปัญหาคล้ายกับ HTML5 Canvas arcs
Phrogz

คุณหมายถึงใช้arcสร้างวงกลมเต็มโดยใช้ส่วนโค้งด้านซ้ายและส่วนโค้งใช่มั้ย ดังนั้น arc ไม่สามารถวาดวงกลมที่สมบูรณ์ได้ ... เพื่อให้มีสองarcเส้นทางที่ต้องการ
nonopolarity

2
@ 能量能量ถูกต้องจำเป็นต้องใช้เส้นทางอาร์คสองเส้นทาง (หรือแฮ็คที่น่าเกลียดของอาร์คหนึ่งไปทางมากที่สุด
Phrogz

1
Illustrator sucks คุณไม่สามารถสร้างวงกลมด้วยเส้นโค้งเบซิเยร์ เฉพาะบางสิ่งที่อยู่ใกล้กับวงกลม แต่ยังไม่เป็นวงกลม
adius

4

เป็นความคิดที่ดีที่การใช้คำสั่ง arc สองคำสั่งเพื่อวาดวงกลมทั้งหมด

ฉันมักจะใช้วงรีหรือองค์ประกอบวงกลมเพื่อวาดวงกลมเต็ม


5
ข้อ จำกัด ที่สำคัญของ svg คือองค์ประกอบรูปร่างไม่สามารถใช้สำหรับพา ธ ข้อความ นี่อาจเป็นแรงจูงใจหลักสำหรับทุกคนที่เข้าชมคำถามนี้
Anthony

1
@ แอนโทนี่ตอนนี้พวกเขาสามารถ Firefox รองรับ textPath กับทุกรูปร่าง ฉันคิดว่า Chrome ก็ทำเช่นกัน
Robert Longson

@RobertLongson - คุณสามารถให้ตัวอย่างหรือลิงค์ไปยังตัวอย่างได้หรือไม่? อยากรู้ว่ามันตัดสินใจว่าเส้นทางข้อความเริ่มต้นที่ใดและอยู่ด้านนอกหรือด้านใน (ฯลฯ ) เมื่อมันถูกวาดโดยไม่มีจุดเริ่มต้นแบบดั้งเดิม
Anthony

@Anthony ดูข้อมูลจำเพาะ SVG 2 เช่นw3.org/TR/SVG2/shapes.html#CircleElementรวมถึงแอตทริบิวต์
Robert Longson

4

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

function(cx, cy, r, deg){
    var theta = deg*Math.PI/180,
        dx = r*Math.cos(theta),
        dy = -r*Math.sin(theta);
    return "M "+cx+" "+cy+"m "+dx+","+dy+"a "+r+","+r+" 0 1,0 "+-2*dx+","+-2*dy+"a "+r+","+r+" 0 1,0 "+2*dx+","+2*dy;
}

นี่คือสิ่งที่ฉันต้องการ ฉันใช้ปลั๊กอิน greensock morph เพื่อ morph เส้นทางวงกลมไปยังเส้นทางสี่เหลี่ยมและต้องการการหมุนเล็กน้อยเพื่อให้มันดูถูกต้อง
RoboKozo

3

สำหรับคนอย่างฉันที่กำลังมองหาคุณลักษณะวงรีเพื่อการแปลงเส้นทาง:

const ellipseAttrsToPath = (rx,cx,ry,cy) =>
`M${cx-rx},${cy}a${rx},${ry} 0 1,0 ${rx*2},0a${rx},${ry} 0 1,0 -${rx*2},0`

2

เขียนเป็นฟังก์ชั่นดูเหมือนว่า:

function getPath(cx,cy,r){
  return "M" + cx + "," + cy + "m" + (-r) + ",0a" + r + "," + r + " 0 1,0 " + (r * 2) + ",0a" + r + "," + r + " 0 1,0 " + (-r * 2) + ",0";
}

2
นี่เป็นคำตอบที่ยอดเยี่ยม! นอกจากนี้เล็กน้อย: เส้นทางนี้ถูกวาดตะวันออก, เหนือ, ตะวันตก, ใต้ หากคุณต้องการให้วงกลมทำตัวเหมือน<circle>กันคุณต้องเปลี่ยนทิศทางเป็นทิศตะวันออกทิศใต้ทิศตะวันตกทิศเหนือ: "M" + cx + "," + cy + "m" + (r) + ",0" + "a" + r + "," + r + " 0 1,1 " + (-r * 2) + ",0" + "a" + r + "," + r + " 0 1,1 " + (r * 2) + ",0" ข้อดีคือตอนนี้stroke-dashoffsetและstartOffsetตอนนี้ทำงานในลักษณะเดียวกัน
ตัด

2

คำตอบเหล่านี้ซับซ้อนเกินไป

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

นี้จะถือว่าพื้นที่ผืนผ้าใบของคุณมีความกว้างและความสูงwh

`M${w*0.5 + radius},${h*0.5}
 A${radius} ${radius} 0 1 0 ${w*0.5 + radius} ${h*0.5001}`

เพียงใช้ธง "ส่วนโค้งยาว" เพื่อให้ธงเต็มเต็ม จากนั้นสร้างส่วนโค้ง 99.9999% เป็นวงกลมเต็ม สายตามันเหมือนกัน หลีกเลี่ยงการใช้ธงปัดโดยเริ่มจากวงกลมที่จุดขวาสุดในวงกลม (รัศมีหนึ่งแนวนอนจากศูนย์กลาง)


1

อีกวิธีหนึ่งก็คือใช้ Cubic Bezier Curves สองเส้น สำหรับคน iOS ที่ใช้pocketSVGซึ่งไม่รู้จักพารามิเตอร์ svg arc

C x1 y1, x2 y2, xy (หรือ c dx1 dy1, dx2 dy2, dx dy)

ลูกบาศก์เบซิเยร์โค้ง

พิกัดชุดสุดท้ายที่นี่ (x, y) เป็นตำแหน่งที่คุณต้องการให้เส้นสิ้นสุด อีกสองคนเป็นจุดควบคุม (x1, y1) เป็นจุดควบคุมสำหรับการเริ่มโค้งของคุณและ (x2, y2) สำหรับจุดสิ้นสุดของเส้นโค้งของคุณ

<path d="M25,0 C60,0, 60,50, 25,50 C-10,50, -10,0, 25,0" />

1

ฉันทำ jsfiddle เพื่อทำที่นี่:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}

function describeArc(x, y, radius, startAngle, endAngle){

var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);

var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

var d = [
    "M", start.x, start.y, 
    "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
].join(" ");

return d;       
}
console.log(describeArc(255,255,220,134,136))

ลิงค์

สิ่งที่คุณต้องทำคือเปลี่ยนอินพุตของ console.log และรับผลลัพธ์ในคอนโซล


นี่คือสิ่งที่ฉันกำลังมองหา คำตอบที่ดีที่สุดที่นี่! โหวตขึ้นผู้ชายคนนี้
MånsDahlström
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.