SVG'nin yay yolu ile daire çizimi


149

Kısa soru: SVG yolunu kullanarak bir çemberin% 99,99'unu çizebiliriz ve bu ortaya çıkar, ancak çemberin% 99,999999999'u olduğunda, çember görünmez. Nasıl düzeltilebilir?

Aşağıdaki SVG yolu bir dairenin% 99,99'unu çizebilir: ( http://jsfiddle.net/DFhUF/1381/ adresinde deneyin ve 4 yay mı yoksa yalnızca 2 yay mı gördüğünüze bakın, ancak IE ise SVG'de değil, VML'de oluşturulmuş, ancak benzer bir sorunu var)

M 100 100 a 50 50 0 1 0 0.00001 0

Ancak bir dairenin% 99,99999999'u olduğunda, hiçbir şey görünmeyecek mi?

M 100 100 a 50 50 0 1 0 0.00000001 0    

Ve bu bir çemberin% 100'ü için de aynıdır (bu hala bir yay, değil mi, sadece tam bir yay)

M 100 100 a 50 50 0 1 0 0 0 

Bu nasıl düzeltilebilir? Bunun nedeni, bir yayın yüzdesi çizmek için bir işlev kullanmam ve çember işlevini kullanmak için% 99,9999 veya% 100 yayı "özel duruma" ihtiyacım olursa, bu biraz saçma olur.

Yine, RaphaelJS kullanan jsfiddle üzerinde bir test durumu http://jsfiddle.net/DFhUF/1381/ adresindedir
(ve eğer IE 8'de VML ise, ikinci daire bile gösterilmeyecektir ... olarak değiştirmelisiniz. 0.01)


Güncelleme:

Bunun nedeni, sistemimizdeki bir puan için bir yay çiziyor olmam, yani 3.3 puan bir çemberin 1 / 3'ünü alıyor. 0.5 yarım daire alır ve 9.9 puan bir dairenin% 99'unu alır. Peki ya sistemimizde 9,99 puan varsa? Bir dairenin% 99,999'una yakın olup olmadığını kontrol etmeli ve buna göre bir arcişlev veya işlev kullanmalı circlemıyım? Peki ya 9,9987 puan? Hangisini kullanmalı? Ne tür puanların "çok tam bir daire" ile eşleneceğini ve bir daire işlevine geçeceğini bilmek ve bir çemberin "belirli bir% 99,9'u" veya bir 9,9987 puan olduğunda yay işlevini kullanmak gülünçtür .


@minitech tabii ki işe yarıyor ... o zaman bir çemberin% 99'u (kabaca konuşursak). Durum, bir çemberin% 98,% 99,% 99,99'unu çizebilmesidir, ancak%
99,9999999

1
Bu bağlantıların her ikisi de aynı şeye gidiyor ve Safari'de sorunsuz çalışıyor.
Mark Bessey

doğru, aynı bağlantı, insanların daha önce test vakasını görmesini istiyorum, bu yüzden bağlantıyı sorunun başına ekliyorum. Doğru safari bunu yapacak, ne kadar güzel ... Chrome ve Firefox yapmayacak ... biraz tuhaf coz Safari ve Chrome'un her ikisi de Webkit ... ama SVG motoru Webkit'e mi bağlı?
nonopolarite

@Marcin nasıl görünüyor? 4 yay mı yoksa 2 yay mı görüyorsunuz? koda baktın mı?
nonopolarite

2
raphael.js yüklenirken çapraz kaynak hatalarını gidermek için, kitaplık olarak dahil edilmiş Raphael ile güncellenmiş bir jsfiddle: jsfiddle.net/DFhUF/1381
ericsoco

Yanıtlar:


36

XAML'nin yayı için aynı. % 99,99 yayı bir ile kapatın ve Zbir daire elde edin!


peki jsfiddle örneğindeki üçüncü durumu kapatıp çalışmasını sağlayabilir misiniz?
kutuplaşmama

değeri 0.0001'e düşürerek, evet. sorun, SVG'nin kendisiyle değil, çeşitli tarayıcının WebKit uygulamasıyla ilgili görünüyor. Ancak SVG / XAMLs Arc bileşeninde bir daire bu şekilde yapılır.
Todd Main

Ahhh, hile yapmak her zaman en iyi çözümdür. Yine de% 100 durumunu ele almalı, ancak tamamen ayrı bir kod dalı yerine% 99,999'a "yuvarlayarak".
SimonR

Demo var mı? Bu cevap yerine gelmiyor
Medet Tleukabiluly

@MedetTleukabiluly yukarıdaki arkı var z, sonuna bir ekleyip bitti. Sıfırları kaldırmanız gerekebilir, örneğin çalışması için en son Chrome'da yazmak zorunda kaldım 0.0001. Yol dizesini nereye koyacağınızı arıyorsanız , MDN'deki Yollar'a bakın .
TWiStErRob

424

Oyunda biraz geç olduğunu biliyorum, ancak bu soruyu yeni olduğu zamanlardan hatırladım ve benzer bir ikilem yaşadım ve biri hala bir tane arıyorsanız, yanlışlıkla "doğru" çözümü buldum:

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

Başka bir deyişle, bu:

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

bununla bir yol olarak elde edilebilir:

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

İşin püf noktası, iki yaya sahip olmaktır, ikincisi ilkinin kaldığı yerden devam eder ve orijinal yay başlangıç ​​noktasına geri dönmek için negatif çapı kullanır.

Tek bir yayda tam bir daire olarak yapılamamasının nedeni (ve ben sadece spekülasyon yapıyorum), ona kendisinden (diyelim ki 150,150) kendisine (150,150) bir yay çizmesini söylemenizdir. "oh, ben zaten oradayım, ark gerekmiyor!" gibi.

Sunduğum çözümün faydaları:

  1. bir çemberden doğrudan bir yola çevirmek kolaydır ve
  2. iki yay çizgisinde örtüşme yoktur (işaretçiler veya desenler vb. kullanıyorsanız sorunlara neden olabilir). İki parça halinde çizilmiş de olsa temiz ve kesintisiz bir çizgidir.

Metin yollarının şekilleri kabul etmesine izin verselerdi bunların hiçbiri önemli değildir. Ama bence bu çözümden kaçınıyorlar çünkü daire gibi şekil öğelerinin teknik olarak bir "başlangıç" noktası yok.

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

Güncelleme:

Yolu bir textPathreferans için kullanıyorsanız ve metnin yayın dış kenarında oluşturulmasını istiyorsanız, aynı yöntemi kullanırsınız, ancak süpürme bayrağını 0'dan 1'e değiştirir, böylece yolun dışını ele alırsınız. içten çok yüzey olarak yol ( 1,0merkezde oturan ve kendi etrafında bir daire çizen 1,1biri olarak, merkezin etrafında yarıçap mesafesinde yürüyen ve tebeşirlerini yanlarında sürükleyen biri olarak düşünün , eğer yardımı olacaksa). Kod yukarıdaki gibi ancak değişiklikle birlikte:

<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, Formüller için teşekkürler. Tabii ki, ilk iki komut birleştirilebilir.
John Gietzen

Genel olarak çözümün netliği ve pratikliği için +1, ancak özellikle "daire gibi şekil öğelerinin teknik olarak bir" başlangıç ​​"noktasına sahip olmadığı için - sorunu ifade etmenin çok net bir yolu.
David John Welsh

11
Sanırım buradaki problem, "oh, ben zaten oradayım, ark gerekmiyor!" Değil, çünkü 1 olarak geniş yay bayrağı sağlayarak motora daha ileri gitmesini istediğinizi açıkça söylüyorsunuz. Asıl sorun, sizin noktanızdan geçme kısıtlamalarını tam olarak alan sonsuz sayıda daire olmasıdır. Bu, 360 * ark istediğinizde motorun neden ne yapacağını bilmediğini açıklıyor. 359.999 için çalışmamasının nedeni, bunun sayısal olarak dengesiz bir hesaplama olması ve teknik olarak istekle eşleşen tam olarak bir daire olsa da, muhtemelen istediğiniz bir daire olmayacaktır
qbolec

@qbolec - Haklısın, geniş yay ve süpürmenin kapalı olması ve yayın hedef olmaması açısından düşünüyordum. Ya da en azından, geniş yay ve süpürme ile bile, bir yayı iki nokta arasındaki eğri olarak tanımlayan bazı temel tasarımların mümkün olması (böylece bir nokta iki kez kullanıldığında, onu daraltılmış tek bir nokta olarak gördü.) Bir noktanın etrafında 360 daire çizme potansiyeli olan yayın görüntüsü mantıklıdır, ancak bir merkez tanımlanırsa tek bir daireye çöker, bu da bir merkez varsa tek bir yay tam bir daire oluşturabilir mi sorusunu gündeme getirir.
Anthony

1
"daire gibi şekil öğelerinin teknik olarak bir" başlangıç ​​"noktası" yoktur. Bu aslında doğru değil. SVG, tüm şekillerin başlangıç ​​noktasının nerede olduğunu kesin olarak belirtir. Çizgi dizilerinin şekiller üzerinde çalışması için bunu yapması gerekir.
Paul LeBeau

19

Anthony'nin çözümüne referans olarak, yolu elde etmek için bir fonksiyon:

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

Tamamen farklı bir yaklaşım:

Svg'de bir yay belirlemek için yollarla uğraşmak yerine, sözde kodda bir daire öğesi alabilir ve bir çizgi-çizgi dizisi belirtebilirsiniz:

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

Basitliği, çoklu yay yolu yöntemine göre temel avantajdır (örneğin, komut dosyası oluştururken yalnızca bir değer girersiniz ve herhangi bir yay uzunluğu için işiniz biter)

Yay, en sağ noktadan başlar ve döndürme dönüşümü kullanılarak kaydırılabilir.

Not: Firefox'ta 90 derece veya daha fazla dönmenin göz ardı edildiği garip bir hata vardır. Yayı yukarıdan başlatmak için şunu kullanın:

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

Bu çözümün yalnızca herhangi bir tür dolgu kullanmadığınız ve yalnızca sapma olmayan bir yay segmentine ihtiyaç duyduğunuz durumlar için geçerli olduğunu unutmayın. Sektörleri çizmek istediğiniz an, aksi takdirde tek bir yol etiketinde işlenebilecek yeni bir svg yol düğümüne bile ihtiyacınız vardır.
mxfh

@mxfh gerçekten değil, r değerinin iki katı vuruş genişliği alırsanız mükemmel sektörler elde edersiniz.
mvds

Bununla stroke-dasharray="0 10cm 5cm 1000cm"birlikte dönüşümü önlemek mümkün
stenci

@stenci evet, ancak dezavantajı, gösterge dizisinde% 0 ile% 100 doldurma arasında ölçeklenecek tek bir parametrenizin olmamasıdır (ki bu benim hedeflerimden biriydi)
mvds

5

Adobe Illustrator, SVG gibi bezier eğrilerini kullanır ve daireler için dört nokta oluşturur. İki eliptik yay komutuyla bir daire oluşturabilirsiniz ... ancak SVG'deki bir daire için bir <circle />:)


"İki eliptik yay komutuyla bir daire oluşturabilirsiniz"? ne demek istiyorsun? Sadece birini kullanamaz mısın? En azından (0,0) 'dan (0,01, 0)' a gitmeyi yaparak bir şey yaratsın. Kullanmak için circle, bunun yerine lütfen sorudaki Güncellemeye bakın .
nonopolarite

Hayır, sadece birini kullanabileceğini sanmıyorum. Bunu yapamayacağınızı gösterdiniz ve HTML5 Canvas yaylarıyla benzer bir sorununuz var.
Phrogz

arcsol yayı ve sağ yayı kullanarak tam bir daire oluşturmak için kullanmayı mı kastediyorsunuz ? Yani ark tam bir daire arc
çizemez

2
@ 動靜 能量 Doğru, iki yay yolu gereklidir (veya yolun çoğuna giden bir yayın çirkin kesilmesi ve ardından düz çizginin sonuna kadar bir yakın yol komutu).
Phrogz

1
Illustrator berbat. Bezier eğrileriyle daire yapamazsınız. Sadece daireye yakın bir şey, ama yine de bir daire değil.
adius

4

Tam bir daire çizmek için iki yay komutunun kullanılması iyi bir fikirdir.

genellikle tam bir daire çizmek için elips veya daire elemanı kullanırım.


5
Svg'nin önemli bir sınırlaması, şekil öğelerinin metin yolları için kullanılamamasıdır. Muhtemelen bu soruyu ziyaret eden herkes için birincil motivasyon budur.
Anthony

1
@Anthony artık yapabilirler. Firefox, herhangi bir şekle karşı textPath'i destekler. Chrome'un da yaptığından şüpheleniyorum.
Robert Longson

@RobertLongson - Bir örnek veya bir örneğe bağlantı verebilir misiniz? Metin yolunun nerede başladığına ve geleneksel bir başlangıç ​​noktası olmadan çizildiğinde dışarıda mı yoksa içeride mi (vb.) Olduğuna nasıl karar verdiğini merak ediyor.
Anthony

@Anthony SVG 2 spesifikasyonuna bakın, örneğin w3.org/TR/SVG2/shapes.html#CircleElement , ayrıca yeni textPath yan özelliği
Robert Longson

4

Anthony ve Anton'un yanıtlarına dayanarak, oluşturulan daireyi genel görünümünü etkilemeden döndürme yeteneğini dahil ettim. Bu, bir animasyon için yolu kullanıyorsanız ve nerede başladığını kontrol etmeniz gerekiyorsa yararlıdır.

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

Bu tam olarak ihtiyacım olan şey. Bir daire yolunu dikdörtgen bir yola dönüştürmek için greensock morph eklentisini kullanıyordum ve doğru görünmesi için hafif bir dönüşe ihtiyacım vardı.
RoboKozo

3

Bir fonksiyon olarak yazıldığında şuna benzer:

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
Bu harika bir cevap! Sadece küçük bir ekleme: Bu yol Doğu, Kuzey, Batı, Güney çizilir. Çemberin tam olarak bir a gibi davranmasını <circle>istiyorsanız, yönü Doğu, Güney, Batı, Kuzey olarak değiştirmelisiniz: "M" + cx + "," + cy + "m" + (r) + ",0" + "a" + r + "," + r + " 0 1,1 " + (-r * 2) + ",0" + "a" + r + "," + r + " 0 1,1 " + (r * 2) + ",0" Avantaj şu stroke-dashoffsetve startOffsetşimdi aynı şekilde çalışıyor.
dokuma tezgahı

3

Yol dönüşümü için elips öznitelikleri arayan benim gibi olanlar için:

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`

3

burada yapmak için bir jsfiddle yaptım:

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

bağlantı

Tek yapmanız gereken, console.log girişini değiştirmek ve sonucu console'da almaktır.


Bu tam olarak aradığım şeydi. En iyi cevap burada! bu adama olumlu oy ver
Måns Dahlström

Hayatımı kurtardın :) Bu harika cevap için teşekkürler! Bütün gün bunu arıyorum!
Soley

2

Bu cevaplar çok fazla karmaşık.

Bunu iki yay oluşturmadan veya farklı koordinat sistemlerine dönüştürmeden yapmanın daha basit bir yolu ..

Bu, tuval alanınızın genişliğe wve yüksekliğe sahip olduğunu varsayar h.

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

Sadece "uzun yay" bayrağını kullanın, böylece tam bayrak doldurulur. Ardından yayları% 99,9999 tam daire yapın. Görsel olarak aynı. Süpürme bayrağını, daireyi dairenin en sağ noktasından (merkezden doğrudan yatay bir yarıçap) başlayarak önleyin.


1

Başka bir yol, iki Kübik Bezier Eğrisi kullanmaktır. Bu , svg arc parametresini tanımayan pocketSVG kullanan iOS kullanıcıları içindir .

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

Kübik Bezier eğrisi

Buradaki son koordinat kümesi (x, y), çizginin bitmesini istediğiniz yerdir. Diğer ikisi kontrol noktalarıdır. (x1, y1) eğrinizin başlangıcı için kontrol noktası ve (x2, y2) eğrinizin bitiş noktasıdır.

<path d="M25,0 C60,0, 60,50, 25,50 C-10,50, -10,0, 25,0" />
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.