Gambar lingkaran dengan jalur busur SVG


147

Pertanyaan pendek: dengan menggunakan jalur SVG, kita bisa menggambar 99,99% dari sebuah lingkaran dan itu muncul, tetapi ketika itu 99,99999999% dari sebuah lingkaran, maka lingkaran itu tidak akan muncul. Bagaimana bisa diperbaiki?

Jalur SVG berikut dapat menggambar 99,99% dari lingkaran: (coba di http://jsfiddle.net/DFhUF/1381/ dan lihat apakah Anda melihat 4 busur atau hanya 2 busur, tetapi perhatikan bahwa jika itu adalah IE, itu adalah diberikan dalam VML, bukan SVG, tetapi memiliki masalah serupa)

M 100 100 a 50 50 0 1 0 0.00001 0

Tetapi ketika 99,99999999% lingkaran, maka tidak ada yang menunjukkan sama sekali?

M 100 100 a 50 50 0 1 0 0.00000001 0    

Dan itu sama dengan 100% lingkaran (itu masih busur, bukan, hanya busur yang sangat lengkap)

M 100 100 a 50 50 0 1 0 0 0 

Bagaimana itu bisa diperbaiki? Alasannya adalah saya menggunakan fungsi untuk menggambar persentase busur, dan jika saya perlu "kasus khusus" busur 99,9999% atau 100% untuk menggunakan fungsi lingkaran, itu akan agak konyol.

Sekali lagi, kasus uji pada jsfiddle menggunakan RaphaelJS ada di http://jsfiddle.net/DFhUF/1381/
(dan jika itu VML di IE 8, bahkan lingkaran kedua tidak akan menunjukkan ... Anda harus mengubahnya ke 0,01)


Memperbarui:

Ini karena saya membuat busur untuk skor di sistem kami, jadi 3,3 poin mendapatkan 1/3 lingkaran. 0,5 mendapat setengah lingkaran, dan 9,9 poin mendapatkan 99% lingkaran. Tetapi bagaimana jika ada skor yang 9,99 dalam sistem kami? Apakah saya harus memeriksa apakah dekat dengan 99,999% lingkaran, dan menggunakan arcfungsi atau circlefungsi yang sesuai? Lalu bagaimana dengan skor 9,9987? Yang mana yang akan digunakan? Sangat konyol untuk mengetahui skor seperti apa yang akan dipetakan ke "lingkaran terlalu lengkap" dan beralih ke fungsi lingkaran, dan ketika itu "99,9%" lingkaran atau skor 9,9987, maka gunakan fungsi busur .


@minitech tentu saja berfungsi ... maka 99% dari sebuah lingkaran (hanya berbicara secara kasar). Kasusnya adalah dapat menggambar 98%, 99%, 99,99% dari sebuah lingkaran, tetapi tidak 99,9999999% atau 100%
nonopolaritas

1
Kedua tautan tersebut menuju ke hal yang sama, dan berfungsi baik di Safari.
Mark Bessey

benar, tautan yang sama, saya hanya ingin orang melihat test case lebih awal jadi saya menambahkan tautan di awal pertanyaan. Safari yang tepat akan melakukannya, alangkah baiknya ... Chrome dan Firefox tidak akan ... agak aneh karena Safari dan Chrome keduanya Webkit ... tetapi apakah mesin SVG bergantung pada Webkit?
nonopolaritas

@ Marsin terlihat baik bagaimana? apakah kamu melihat 4 busur atau 2 busur? apakah Anda melihat kode itu?
nonopolaritas

2
jsfiddle yang diperbarui dengan Raphael dimasukkan sebagai perpustakaan, untuk menyiasati kesalahan lintas asal memuat raphael.js: jsfiddle.net/DFhUF/1381
ericsoco

Jawaban:


35

Sama untuk busur XAML. Cukup tutup busur 99,99% dengan Zdan Anda punya lingkaran!


Jadi bisakah Anda menutup kasus ketiga dalam sampel jsfiddle dan membuatnya bekerja?
nonopolaritas

dengan menurunkan nilai ke 0,0001, ya. masalah ini terdengar terkait dengan implementasi berbagai WebKit browser, bukan SVG itu sendiri. Tapi itu adalah bagaimana Anda membuat lingkaran di komponen Arc SVG / XAMLs.
Todd Main

Ahhh, curang, selalu solusi terbaik. Masih perlu menangani case 100%, tetapi hanya dengan "membulatkan ke bawah" menjadi 99,999% daripada dengan seluruh cabang kode yang terpisah.
SimonR

Ada demo? Jawaban ini tidak memenuhi
Medet Tleukabiluly

@ Matetleukabiluly Anda memiliki busur di atas dalam pertanyaan, tambahkan zdi akhir dan selesai. Anda mungkin harus menghapus nol, misalnya di Chrome terbaru yang harus saya tulis 0.0001untuk membuatnya berfungsi. Jika Anda mencari tempat untuk meletakkan string path, lihat Paths di MDN .
TWiStErRob

416

Saya tahu ini agak terlambat dalam permainan, tetapi saya ingat pertanyaan ini sejak masih baru dan saya memiliki dillemma yang sama, dan saya tidak sengaja menemukan solusi "benar", jika ada yang masih mencari satu:

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

Dengan kata lain, ini:

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

dapat dicapai sebagai jalur dengan ini:

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

Triknya adalah memiliki dua busur, yang kedua mengambil tempat yang pertama ditinggalkan dan menggunakan diameter negatif untuk kembali ke titik awal busur asli.

Alasan itu tidak dapat dilakukan sebagai lingkaran penuh dalam satu busur (dan saya hanya berspekulasi) adalah karena Anda akan menyuruhnya untuk menggambar busur dari dirinya sendiri (misalkan 150.150) ke dirinya sendiri (150.150), yang dibuatnya sebagai "oh, aku sudah ada di sana, tidak perlu busur!".

Manfaat dari solusi yang saya tawarkan adalah:

  1. mudah untuk menerjemahkan dari lingkaran langsung ke jalur, dan
  2. tidak ada tumpang tindih di dua garis busur (yang dapat menyebabkan masalah jika Anda menggunakan spidol atau pola, dll). Ini adalah garis kontinu yang bersih, meskipun digambar dalam dua bagian.

Tak satu pun dari ini akan menjadi masalah jika mereka hanya mengizinkan textpath untuk menerima bentuk. Tapi saya pikir mereka menghindari solusi itu karena elemen bentuk seperti lingkaran secara teknis tidak memiliki titik "awal".

demo jsfiddle: http://jsfiddle.net/crazytonyi/mNt2g/

Memperbarui:

Jika Anda menggunakan path untuk textPathreferensi dan Anda ingin teks untuk di-render di tepi luar arc, Anda akan menggunakan metode yang sama persis tetapi mengubah flag-sweep dari 0 ke 1 sehingga memperlakukan bagian luar dari jalanlah sebagai permukaan alih-alih bagian dalam (anggap 1,0sebagai seseorang yang duduk di tengah dan menggambar lingkaran di sekitar diri mereka sendiri, sementara 1,1sebagai seseorang berjalan di sekitar pusat pada jarak radius dan menyeret kapur mereka di samping mereka, jika itu bisa membantu). Berikut adalah kode seperti di atas tetapi dengan perubahan:

<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, Terima kasih atas rumusnya. Tentu saja, dua perintah pertama dapat dikonsolidasikan.
John Gietzen

+1 untuk kejelasan dan kepraktisan solusi secara umum, tetapi terutama untuk 'elemen bentuk seperti lingkaran secara teknis tidak memiliki titik "awal" - cara yang jelas untuk mengekspresikan masalah.
David John Welsh

11
Saya pikir masalahnya di sini bukanlah bahwa "oh, saya sudah ada di sana, tidak perlu busur!", Karena dengan memberikan 1 sebagai busur besar, Anda secara eksplisit memberi tahu mesin bahwa Anda ingin melangkah lebih jauh. Masalah sebenarnya adalah bahwa ada banyak lingkaran tanpa batas yang memenuhi batasan melewati titik Anda. Ini menjelaskan mengapa ketika Anda ingin 360 * arc, maka mesin tidak tahu apa yang harus dilakukan. Alasannya tidak berfungsi untuk 359.999 adalah bahwa ini adalah perhitungan yang tidak stabil secara numerik, dan sementara secara teknis ada satu lingkaran yang cocok dengan permintaan, mungkin itu bukan yang Anda inginkan
qbolec

@ qbolec - Anda benar, saya berpikir tentang busur besar dan sapuan lepas dan busur tidak memiliki tujuan. Atau paling tidak bahkan dengan busur besar dan sapuan diaktifkan bahwa ada beberapa desain mendasar yang mendefinisikan busur sebagai kurva antara dua titik (sehingga dengan satu titik digunakan dua kali, ia melihatnya sebagai titik tunggal yang runtuh.) visi busur berpotensi menggambar 360 lingkaran di sekitar satu titik masuk akal, meskipun itu akan runtuh ke satu lingkaran jika pusat didefinisikan, yang menimbulkan pertanyaan apakah busur tunggal dapat membuat lingkaran penuh jika pusat itu ada?
Anthony

1
msgstr "elemen bentuk seperti lingkaran secara teknis tidak memiliki" titik awal "". Ini sebenarnya tidak benar. Spesifikasi SVG secara mutlak menentukan titik awal semua bentuk. Itu harus dilakukan agar array dasbor bekerja pada bentuk.
Paul LeBeau

17

Mengacu pada solusi Anthony, berikut adalah fungsi untuk mendapatkan jalur:

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

Pendekatan yang sama sekali berbeda:

Alih-alih mengutak-atik path untuk menentukan arc di svg, Anda juga bisa mengambil elemen lingkaran dan menentukan stroke-dasharray, dalam kode pseudo:

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

Kesederhanaannya adalah keunggulan utama dibandingkan metode jalur-busur-ganda (mis. Saat membuat skrip, Anda hanya memasukkan satu nilai dan Anda selesai untuk panjang busur apa pun)

Busur dimulai pada titik paling kanan, dan dapat digeser menggunakan rotate transform.

Catatan: Firefox memiliki bug aneh di mana rotasi lebih dari 90 derajat atau lebih diabaikan. Jadi untuk memulai lengkungan dari atas, gunakan:

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

Ketahuilah bahwa solusi ini hanya berlaku untuk kasus-kasus di mana Anda tidak menggunakan jenis isian apa pun dan Anda hanya perlu segmen busur tanpa penyimpangan. Saat Anda ingin menggambar sektor, Anda bahkan memerlukan node path svg baru, yang sebaliknya dapat ditangani dalam tag path tunggal.
mxfh

@ mxfh tidak terlalu, jika Anda mengambil lebar selisih dua kali lipat nilai r Anda mendapatkan sektor yang sempurna.
mvds

Dengan stroke-dasharray="0 10cm 5cm 1000cm"dimungkinkan untuk menghindari transformasi
stenci

@stenci ya, tetapi downside adalah bahwa Anda tidak dapat memiliki satu parameter di dasharray untuk skala dari 0% hingga 100% isi (yang merupakan salah satu tujuan saya)
mvds

5

Adobe Illustrator menggunakan kurva bezier seperti SVG, dan untuk lingkaran itu menciptakan empat poin. Anda dapat membuat lingkaran dengan dua perintah busur elips ... tapi kemudian untuk lingkaran di SVG saya akan menggunakan <circle />:)


"Anda dapat membuat lingkaran dengan dua perintah busur elips"? maksud kamu apa? Tidak bisakah kamu menggunakan satu saja? Setidaknya dengan membuat untuk beralih dari (0,0) ke (0,01, 0) sehingga menghasilkan sesuatu. Untuk menggunakan circle, silakan lihat Pembaruan dalam pertanyaan.
nonopolaritas

Tidak, saya pikir Anda tidak bisa menggunakan satu saja. Anda telah menunjukkan bahwa Anda tidak bisa, dan Anda memiliki masalah yang serupa dengan busur Kanvas HTML5.
Phrogz

maksud Anda, menggunakan arcuntuk membuat lingkaran penuh dengan menggunakan busur kiri dan busur kanan? Jadi arc tidak dapat menggambar lingkaran lengkap ... untuk melakukannya dua arcjalur diperlukan
nonopolaritas

2
@ 動靜 能量 Benar, dua jalur busur diperlukan (atau retakan jelek dari salah satu busur yang paling jalan dan kemudian perintah closepath untuk garis lurus ke ujung).
Phrogz

1
Ilustrator menyebalkan. Anda tidak dapat membuat lingkaran dengan kurva bezier. Hanya sesuatu yang dekat dengan lingkaran, tetapi masih bukan lingkaran.
adius

4

Ide bagus menggunakan perintah dua busur untuk menggambar lingkaran penuh.

biasanya, saya menggunakan elemen elips atau lingkaran untuk menggambar lingkaran penuh.


5
Keterbatasan utama svg adalah elemen bentuk tidak bisa digunakan untuk jalur teks. Ini kemungkinan merupakan motivasi utama bagi semua orang yang mengunjungi pertanyaan ini.
Anthony

1
@Anthony mereka sekarang bisa. Firefox mendukung textPath terhadap bentuk apa pun. Saya kira Chrome juga.
Robert Longson

@RobertLongson - Bisakah Anda memberikan contoh atau tautan ke sebuah contoh? Ingin tahu bagaimana ia memutuskan di mana jalur teks dimulai dan apakah itu di luar atau di dalam (dll) ketika digambar tanpa titik awal tradisional.
Anthony

@Anthony Lihat spesifikasi SVG 2, mis. W3.org/TR/SVG2/shapes.html#CircleElement , juga atribut sisi
Robert Longson

4

Membangun berdasarkan jawaban Anthony dan Anton, saya memasukkan kemampuan untuk memutar lingkaran yang dihasilkan tanpa memengaruhi keseluruhan penampilannya. Ini berguna jika Anda menggunakan jalur untuk animasi dan Anda perlu mengontrol dari mana ia dimulai.

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

Inilah yang saya butuhkan. Saya menggunakan plugin morph greensock untuk mengubah jalur lingkaran ke jalur persegi panjang dan membutuhkan sedikit rotasi agar terlihat benar.
RoboKozo

3

Bagi mereka seperti saya yang mencari atribut elips untuk konversi jalur:

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

Ditulis sebagai fungsi, tampilannya seperti ini:

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
Ini jawaban yang bagus! Tambahan kecil: Jalan ini ditarik ke Timur, Utara, Barat, Selatan. Jika Anda ingin lingkaran berperilaku persis seperti <circle>, Anda harus mengubah arah ke Timur, Selatan, Barat, Utara: "M" + cx + "," + cy + "m" + (r) + ",0" + "a" + r + "," + r + " 0 1,1 " + (-r * 2) + ",0" + "a" + r + "," + r + " 0 1,1 " + (r * 2) + ",0" Keuntungannya adalah stroke-dashoffsetdan startOffsetsekarang bekerja dengan cara yang sama.
Tenun

2

Jawaban ini terlalu rumit.

Cara sederhana untuk melakukan ini tanpa membuat dua lengkok atau mengonversi ke sistem koordinat yang berbeda ..

Ini mengasumsikan area kanvas Anda memiliki lebar wdan tinggi h.

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

Cukup gunakan bendera "busur panjang", sehingga bendera penuh terisi. Kemudian buatlah busur 99,9999% lingkaran penuh. Secara visual itu sama. Hindari bendera sapuan dengan hanya memulai lingkaran di titik paling kanan dalam lingkaran (satu jari-jari horizontal langsung dari pusat)


1

Cara lain adalah dengan menggunakan dua Kurva Bezier Kubik. Itu untuk iOS yang menggunakan pocketSVG yang tidak mengenali parameter arc svg.

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

Kurva Bezier kubik

Set koordinat terakhir di sini (x, y) adalah di mana Anda ingin mengakhiri garis. Dua lainnya adalah titik kontrol. (x1, y1) adalah titik kontrol untuk awal kurva Anda, dan (x2, y2) untuk titik akhir kurva Anda.

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

1

Saya membuat jsfiddle untuk melakukannya di sini:

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

tautan

yang perlu Anda lakukan adalah mengubah input dari console.log dan mendapatkan hasilnya di console


Ini persis apa yang saya cari. Jawaban terbaik di sini! buktikan orang ini
Måns Dahlström
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.