Vẽ vòng tròn với đường vòng cung của SVG


146

Câu hỏi ngắn: sử dụng đường dẫn SVG, chúng ta có thể vẽ 99,99% hình tròn và nó hiển thị, nhưng khi nó là 99.99999999% hình tròn thì hình tròn sẽ không hiển thị. Làm thế nào nó có thể được cố định?

Đường dẫn SVG sau đây có thể vẽ 99,99% của một vòng tròn: (hãy thử trên http://jsfiddle.net/DFhUF/1381/ và xem nếu bạn thấy 4 cung hoặc chỉ 2 cung, nhưng lưu ý rằng nếu đó là IE, thì đó là được hiển thị trong VML, không phải SVG, nhưng có vấn đề tương tự)

M 100 100 a 50 50 0 1 0 0.00001 0

Nhưng khi nó là 99.99999999% của một vòng tròn, thì không có gì sẽ hiển thị cả?

M 100 100 a 50 50 0 1 0 0.00000001 0    

Và điều đó cũng tương tự với 100% hình tròn (nó vẫn là một vòng cung, không phải vậy, chỉ là một vòng cung rất hoàn chỉnh)

M 100 100 a 50 50 0 1 0 0 0 

Làm thế nào mà có thể được sửa chữa? Lý do là tôi sử dụng một hàm để vẽ tỷ lệ phần trăm của một cung và nếu tôi cần "trường hợp đặc biệt" một cung 99,9999% hoặc 100% để sử dụng hàm vòng tròn, điều đó thật là ngớ ngẩn.

Một lần nữa, một trường hợp thử nghiệm trên jsfiddle sử dụng RaphaelJS có tại http://jsfiddle.net/DFhUF/1381/
(và nếu đó là VML trên IE 8, ngay cả vòng tròn thứ hai sẽ không hiển thị ... bạn phải đổi nó thành 0,01)


Cập nhật:

Điều này là do tôi đang hiển thị một vòng cung cho điểm trong hệ thống của chúng tôi, vì vậy 3,3 điểm nhận được 1/3 vòng tròn. 0,5 được nửa vòng tròn và 9,9 điểm nhận được 99% vòng tròn. Nhưng nếu có điểm 9,99 trong hệ thống của chúng tôi thì sao? Tôi có phải kiểm tra xem nó có gần 99,999% của một vòng tròn không, và sử dụng một arcchức năng hoặc một circlechức năng tương ứng? Vậy còn điểm 9,9987 thì sao? Nên dùng cái nào? Thật lố bịch khi cần biết loại điểm nào sẽ ánh xạ tới "vòng tròn quá hoàn chỉnh" và chuyển sang chức năng vòng tròn và khi đó là "99,9%" nhất định của vòng tròn hoặc điểm 9,9987, sau đó sử dụng chức năng cung tròn .


@minitech tất nhiên là nó hoạt động ... nó là 99% của một vòng tròn (chỉ nói đại khái). Trường hợp là nó có thể vẽ 98%, 99%, 99,99% của một vòng tròn, nhưng không phải là 99.9999999% hoặc 100%
phân cực

1
Cả hai liên kết này đều đi đến cùng một thứ và nó hoạt động tốt trong Safari.
Mark Bessey

đúng, cùng một liên kết, tôi chỉ muốn mọi người xem trường hợp thử nghiệm sớm hơn vì vậy tôi thêm liên kết ở đầu câu hỏi. Phải safari sẽ làm điều đó, thật tuyệt ... Chrome và Firefox sẽ không ... loại coz lạ và Safari đều là Webkit ... nhưng công cụ SVG có phụ thuộc vào Webkit không?
phân cực

@Marcin trông ổn thế nào? Bạn có thấy 4 cung hay 2 cung không? bạn thậm chí đã nhìn vào mã?
phân cực

2
một jsfiddle được cập nhật với Raphael được bao gồm như một thư viện, để khắc phục lỗi tải nguồn gốc chéo raphael.js: jsfiddle.net/DFhUF/1381
ericsoco

Câu trả lời:


35

Tương tự cho cung của XAML. Chỉ cần đóng vòng cung 99,99% với một Zvà bạn đã có một vòng tròn!


Vì vậy, bạn có thể đóng trường hợp thứ ba trong mẫu jsfiddle và làm cho nó hoạt động?
phân cực

với việc hạ giá trị xuống 0,0001, vâng. vấn đề âm thanh liên quan đến việc triển khai WebKit của nhiều trình duyệt khác nhau, chứ không phải bản thân SVG. Nhưng đó là cách bạn tạo một vòng tròn trong thành phần Arc SVG / XAMLs.
Todd Main

Ahhh, gian lận, luôn luôn là giải pháp tốt nhất. Vẫn cần xử lý trường hợp 100%, nhưng chỉ bằng cách "làm tròn xuống" đến 99,999% thay vì với một nhánh mã hoàn toàn riêng biệt.
SimonR

Có bản demo nào không? Câu trả lời này không đáp ứng
Medet Tleukabiluly

@MedetTleukabiluly bạn có vòng cung ở trên trong câu hỏi, thêm một câu zở cuối và thực hiện. Bạn có thể phải xóa các số 0, ví dụ như trong Chrome mới nhất tôi phải viết 0.0001để làm cho nó hoạt động. Nếu bạn đang tìm nơi đặt chuỗi đường dẫn, hãy xem Đường dẫn trên MDN .
TWiStErRob

415

Tôi biết rằng nó hơi muộn trong trò chơi, nhưng tôi đã nhớ câu hỏi này từ khi nó còn mới và tôi có một vấn đề tương tự, và tôi vô tình tìm thấy giải pháp "đúng", nếu bất cứ ai vẫn đang tìm kiếm một:

<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
    "
/>

Nói cách khác, điều này:

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

có thể đạt được như một con đường với điều này:

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

Bí quyết là có hai cung, cái thứ hai chọn nơi đầu tiên rời khỏi và sử dụng đường kính âm để quay lại điểm bắt đầu vòng cung ban đầu.

Lý do nó không thể được thực hiện dưới dạng một vòng tròn đầy đủ trong một cung (và tôi chỉ đang suy đoán) là vì bạn sẽ bảo nó vẽ một cung từ chính nó (giả sử 150.150) cho chính nó (150.150), mà nó biểu hiện như "oh, tôi đã ở đó, không cần cung!".

Lợi ích của giải pháp tôi cung cấp là:

  1. thật dễ dàng để dịch từ một vòng tròn trực tiếp sang một đường dẫn và
  2. không có sự trùng lặp trong hai đường cung (có thể gây ra sự cố nếu bạn đang sử dụng các dấu hoặc mẫu, v.v.). Đó là một dòng liên tục sạch sẽ, mặc dù được vẽ thành hai mảnh.

Không ai trong số này có vấn đề nếu họ chỉ cho phép các đường dẫn văn bản chấp nhận hình dạng. Nhưng tôi nghĩ họ đang tránh giải pháp đó vì các yếu tố hình dạng như vòng tròn về mặt kỹ thuật không có điểm "bắt đầu".

bản demo jsfiddle: http://jsfiddle.net/crazytonyi/mNt2g/

Cập nhật:

Nếu bạn đang sử dụng đường dẫn cho một textPaththam chiếu và bạn muốn văn bản hiển thị ở cạnh ngoài của cung, bạn sẽ sử dụng cùng một phương thức nhưng thay đổi cờ quét từ 0 thành 1 để nó xử lý bên ngoài của đi như bề mặt thay vì bên trong (nghĩ về 1,0một người ngồi ở trung tâm và vẽ một vòng tròn xung quanh mình, trong khi 1,1ai đó đi quanh trung tâm ở khoảng cách bán kính và kéo phấn của họ bên cạnh họ, nếu đó là bất kỳ sự giúp đỡ nào). Đây là mã như trên nhưng với sự thay đổi:

<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, Cảm ơn các công thức. Tất nhiên, hai lệnh đầu tiên có thể được hợp nhất.
John Gietzen

+1 cho sự rõ ràng và thực tế của giải pháp nói chung, nhưng đặc biệt đối với 'các yếu tố hình dạng như vòng tròn về mặt kỹ thuật không có "điểm bắt đầu" - một cách rõ ràng để diễn đạt vấn đề.
David John Welsh

11
Tôi nghĩ vấn đề ở đây không phải là "ồ, tôi đã ở đó, không cần cung nào!", Vì bằng cách cung cấp 1 như cờ hình cung lớn, bạn nói rõ với động cơ rằng bạn muốn nó đi xa hơn. Vấn đề thực sự là có vô số vòng tròn có đầy đủ các hạn chế để vượt qua điểm của bạn. Điều này giải thích tại sao khi bạn muốn cung 360 * thì động cơ không biết phải làm gì. Lý do nó không hoạt động cho 359.999 là vì đây là tính toán không ổn định về mặt số và trong khi về mặt kỹ thuật chính xác là một vòng tròn phù hợp với yêu cầu, có lẽ nó sẽ không phải là một vòng tròn bạn muốn
qbolec

@qbolec - Bạn nói đúng, tôi đã suy nghĩ về mặt vòng cung lớn và quét bị tắt và vòng cung không có đích đến. Hoặc ít nhất là ngay cả với cung lớn và quét cho phép có một số thiết kế cơ bản xác định một cung là đường cong giữa hai điểm (để với một điểm được sử dụng hai lần, nó thấy đó là một điểm bị sụp đổ.) tầm nhìn của vòng cung có khả năng vẽ 360 vòng tròn xung quanh một điểm có ý nghĩa, mặc dù nó sẽ sụp xuống một vòng tròn nếu một trung tâm được xác định, điều này đặt ra câu hỏi liệu một cung có thể tạo thành một vòng tròn đầy đủ nếu một trung tâm tồn tại?
Anthony

1
"Các yếu tố hình dạng như vòng tròn về mặt kỹ thuật không có" điểm bắt đầu ". Điều này không thực sự đúng. Thông số kỹ thuật SVG chỉ định hoàn toàn điểm bắt đầu của tất cả các hình dạng. Nó phải làm như vậy để các mảng dash hoạt động trên các hình dạng.
Paul LeBeau

17

Liên quan đến giải pháp của Anthony, đây là một chức năng để có được đường dẫn:

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

Một cách tiếp cận hoàn toàn khác nhau:

Thay vì loay hoay với các đường dẫn để chỉ định một cung trong svg, bạn cũng có thể lấy một phần tử vòng tròn và chỉ định một dấu gạch ngang, trong mã giả:

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" />

Tính đơn giản của nó là lợi thế chính so với phương pháp nhiều đường cung (ví dụ: khi tạo kịch bản, bạn chỉ cắm một giá trị và bạn đã hoàn thành bất kỳ độ dài cung nào)

Vòng cung bắt đầu tại điểm ngoài cùng bên phải và có thể được dịch chuyển bằng cách sử dụng biến đổi xoay.

Lưu ý: Firefox có một lỗi kỳ lạ trong đó các góc quay trên 90 độ trở lên bị bỏ qua. Vì vậy, để bắt đầu vòng cung từ đầu, sử dụng:

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

lưu ý rằng giải pháp này chỉ áp dụng cho các trường hợp bạn không sử dụng bất kỳ loại điền nào và bạn chỉ cần một đoạn cung không có độ lệch. Thời điểm bạn muốn vẽ các lĩnh vực bạn thậm chí cần một nút đường dẫn svg mới, có thể được xử lý trong một thẻ đường dẫn duy nhất.
mxfh

@mxfh không thực sự, nếu bạn lấy chiều rộng gấp đôi giá trị của r, bạn sẽ có được các cung hoàn hảo.
mvds

Với stroke-dasharray="0 10cm 5cm 1000cm"nó có thể tránh được sự biến đổi
stenci

@stenci có, nhưng nhược điểm là bạn không thể có một tham số trong bảng điều khiển để chia tỷ lệ từ 0% đến 100% (đó là một trong những mục tiêu của tôi)
mvds 27/2/2016

5

Adobe Illustrator sử dụng các đường cong bezier như SVG và đối với các vòng tròn, nó tạo ra bốn điểm. Bạn có thể tạo một vòng tròn với hai lệnh cung hình elip ... nhưng sau đó đối với một vòng tròn trong SVG tôi sẽ sử dụng một <circle />:)


"Bạn có thể tạo một vòng tròn với hai lệnh cung hình elip"? Ý anh là gì? Bạn có thể chỉ sử dụng một? Ít nhất bằng cách thực hiện để đi từ (0,0) đến (0,01, 0) để nó hiển thị một cái gì đó. Để sử dụng circle, vui lòng xem Cập nhật trong câu hỏi thay thế.
phân cực

Không, tôi không nghĩ bạn có thể sử dụng chỉ một. Bạn đã cho thấy rằng bạn không thể và bạn gặp vấn đề tương tự với các vòng cung HTML5 Canvas.
Phrogz

ý bạn là, sử dụng arcđể tạo một vòng tròn đầy đủ bằng cách sử dụng cung bên trái và cung bên phải? Vì vậy, cung không thể vẽ một vòng tròn hoàn chỉnh ... để thực hiện hai arcđường dẫn là cần thiết
phân cực

2
@ 動靜 Đúng, cần có hai đường cung (hoặc hack xấu xí của một cung đi gần hết quãng đường và sau đó là lệnh closepath để đi thẳng đến cuối).
Phrogz

1
Illustrator hút. Bạn không thể tạo một vòng tròn với các đường cong bezier. Chỉ một cái gì đó gần với một vòng tròn, nhưng vẫn không phải là một vòng tròn.
adius

4

Đó là một ý tưởng tốt khi sử dụng lệnh hai cung để vẽ một vòng tròn đầy đủ.

thông thường, tôi sử dụng phần tử hình elip hoặc hình tròn để vẽ một vòng tròn đầy đủ.


5
Một hạn chế lớn của svg là các yếu tố hình dạng không thể được sử dụng cho đường dẫn văn bản. Đây có thể là động lực chính cho tất cả những người truy cập câu hỏi này.
Anthony

1
@Anthony họ có thể bây giờ. Firefox hỗ trợ textPath chống lại mọi hình dạng. Tôi nghi ngờ Chrome cũng vậy.
Robert Longson

@RobertLongson - Bạn có thể cung cấp một ví dụ hoặc liên kết đến một ví dụ không? Tò mò làm thế nào nó quyết định nơi đường dẫn văn bản bắt đầu và liệu nó ở bên ngoài hay bên trong (vv) khi nó được vẽ mà không có điểm bắt đầu truyền thống.
Anthony

@Anthony Xem đặc tả SVG 2, ví dụ w3.org/TR/SVG2/shapes.html#CircleEuity , cũng là thuộc tính bên
Robert Longson

4

Dựa trên câu trả lời của Anthony và Anton, tôi đã kết hợp khả năng xoay vòng tròn được tạo mà không ảnh hưởng đến diện mạo chung của nó. Điều này hữu ích nếu bạn đang sử dụng đường dẫn cho hình ảnh động và bạn cần kiểm soát nơi nó bắt đầu.

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;
}

Đây chính xác là những gì tôi cần. Tôi đã sử dụng plugin morph morph để biến một đường tròn thành đường dẫn hình chữ nhật và cần một góc xoay nhẹ để làm cho nó trông vừa phải.
RoboKozo

3

Đối với những người như tôi, những người đang tìm kiếm một thuộc tính hình elip để chuyển đổi đường dẫn:

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

Được viết như một hàm, nó trông như thế này:

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
Đây là một câu trả lời tuyệt vời! Chỉ cần một bổ sung nhỏ: Con đường này được vẽ Đông, Bắc, Tây, Nam. Nếu bạn muốn vòng tròn hoạt động chính xác như a <circle>, bạn phải đổi hướng sang Đông, Nam, Tây, Bắc: "M" + cx + "," + cy + "m" + (r) + ",0" + "a" + r + "," + r + " 0 1,1 " + (-r * 2) + ",0" + "a" + r + "," + r + " 0 1,1 " + (r * 2) + ",0" Ưu điểm là stroke-dashoffsetstartOffsetbây giờ hoạt động theo cùng một cách.
loominade

2

Những câu trả lời quá phức tạp.

Một cách đơn giản hơn để làm điều này mà không cần tạo hai cung hoặc chuyển đổi sang các hệ tọa độ khác nhau ..

Điều này giả định khu vực vải của bạn có chiều rộng wvà chiều cao h.

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

Chỉ cần sử dụng cờ "vòng cung dài", như vậy cờ đầy đủ. Sau đó làm cho các cung tròn 99,9999% là vòng tròn đầy đủ. Trực quan nó là như nhau. Tránh cờ quét bằng cách chỉ bắt đầu vòng tròn ở điểm ngoài cùng bên phải trong vòng tròn (một bán kính nằm ngang trực tiếp từ tâm).


1

Một cách khác là sử dụng hai Curves Bezier. Đó là cho những người dùng iOS sử dụng PocketSVG không nhận ra tham số cung tròn Svg .

C x1 y1, x2 y2, xy (hoặc c dx1 dy1, dx2 dy2, dx dy)

Đường cong Bezier

Tập hợp tọa độ cuối cùng ở đây (x, y) là nơi bạn muốn dòng kết thúc. Hai cái còn lại là điểm kiểm soát. (x1, y1) là điểm kiểm soát bắt đầu đường cong của bạn và (x2, y2) cho điểm cuối của đường cong của bạn.

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

1

tôi đã tạo một jsfiddle để làm điều đó ở đây:

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

liên kết

tất cả những gì bạn cần làm là thay đổi đầu vào của console.log và nhận kết quả trong console


Đây chính xác là những gì tôi đang tìm kiếm. Câu trả lời tốt nhất ở đây! upvote anh chàng này
Måns Dahlström
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.