Disegno circolare con il percorso dell'arco di SVG


147

Domanda breve: usando il percorso SVG, possiamo disegnare il 99,99% di un cerchio e questo si presenta, ma quando è il 99.99999999% di un cerchio, allora il cerchio non verrà visualizzato. Come può essere aggiustato?

Il seguente percorso SVG può disegnare il 99,99% di un cerchio: (provalo su http://jsfiddle.net/DFhUF/1381/ e vedi se vedi 4 archi o solo 2 archi, ma nota che se è IE, è renderizzato in VML, non in SVG, ma presenta un problema simile)

M 100 100 a 50 50 0 1 0 0.00001 0

Ma quando è il 99.99999999% di un cerchio, allora nulla mostrerà affatto?

M 100 100 a 50 50 0 1 0 0.00000001 0    

E questo è lo stesso con il 100% di un cerchio (è ancora un arco, no, solo un arco molto completo)

M 100 100 a 50 50 0 1 0 0 0 

Come può essere risolto? Il motivo è che uso una funzione per disegnare una percentuale di un arco e se ho bisogno di "caso speciale" un arco del 99,9999% o del 100% per usare la funzione cerchio, sarebbe un po 'sciocco.

Ancora una volta, un caso di prova su jsfiddle usando RaphaelJS è su http://jsfiddle.net/DFhUF/1381/
(e se è VML su IE 8, anche il secondo cerchio non mostrerà ... devi cambiarlo in 0.01)


Aggiornare:

Questo perché sto realizzando un arco per un punteggio nel nostro sistema, quindi 3,3 punti ottengono 1/3 di un cerchio. 0,5 ottiene mezzo cerchio e 9,9 punti ottengono il 99% di un cerchio. Ma cosa succede se ci sono punteggi che sono 9.99 nel nostro sistema? Devo verificare se è vicino al 99,999% di un cerchio e utilizzare una arcfunzione o una circlefunzione di conseguenza? E che dire di un punteggio di 9.9987? Quale usare? È ridicolo dover sapere quale tipo di punteggi verrà mappato su un "cerchio troppo completo" e passare a una funzione di cerchio, e quando è "un certo 99,9%" di un cerchio o un punteggio di 9.9987, quindi utilizzare la funzione arco .


@minitech ovviamente funziona ... è quindi il 99% di un cerchio (in parole povere). Il caso è che può disegnare il 98%, il 99%, il 99,99% di un cerchio, ma non il 99.9999999% o il 100%
polarità

1
Entrambi questi collegamenti vanno alla stessa cosa, e funziona benissimo in Safari.
Mark Bessey,

giusto, stesso link, voglio solo che le persone vedano il test case prima, quindi aggiungo il link all'inizio della domanda. Giusto Safari lo farà, che bello ... Chrome e Firefox non ... tipo di strano coz Safari e Chrome sono entrambi Webkit ... ma il motore SVG dipende da Webkit?
polarità

@Marcin sembra a posto come? vedi 4 archi o 2 archi? hai anche guardato il codice?
polarità

2
un jsfiddle aggiornato con Raphael incluso come libreria, per aggirare l'errore tra le origini durante il caricamento di raphael.js: jsfiddle.net/DFhUF/1381
ericsoco,

Risposte:


35

Lo stesso per l'arco di XAML. Chiudi l'arco del 99,99% con una Ze hai un cerchio!


quindi puoi chiudere il terzo caso nell'esempio jsfiddle e farlo funzionare?
polarità

con la riduzione del valore a 0,0001, sì. il problema sembra correlato all'implementazione di WebKit da parte dei vari browser, non dello stesso SVG. Ma è così che crei un cerchio nel componente Arc di SVG / XAMLs.
Todd Main,

Ahhh, barare, sempre la soluzione migliore. Devo ancora gestire il caso del 100%, ma semplicemente "arrotondando per difetto" al 99,999% anziché con un intero ramo di codice separato.
SimonR,

Qualche demo? Questa risposta non soddisfa
Medet Tleukabiluly,

@MedetTleukabiluly hai l'arco sopra nella domanda, aggiungi za alla fine e fatto. Potrebbe essere necessario rimuovere gli zero, ad esempio nell'ultimo Chrome che ho dovuto scrivere 0.0001per farlo funzionare. Se stai cercando dove inserire la stringa del percorso, guarda Paths su MDN .
TWiStErRob

416

So che è un po 'tardi nel gioco, ma mi sono ricordato di questa domanda da quando era nuovo e avevo un dillemma simile, e per caso ho trovato la soluzione "giusta", se qualcuno ne sta ancora cercando uno:

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

In altre parole, questo:

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

può essere raggiunto come un percorso con questo:

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

Il trucco è avere due archi, il secondo riprendendo da dove il primo si era interrotto e usando il diametro negativo per tornare al punto di inizio dell'arco originale.

Il motivo per cui non può essere fatto come un cerchio completo in un arco (e sto solo ipotizzando) è perché gli diresti di disegnare un arco da se stesso (diciamo 150.150) a se stesso (150.150), che rende come "oh, sono già lì, nessun arco necessario!".

I vantaggi della soluzione che sto offrendo sono:

  1. è facile tradurre da un cerchio direttamente in un percorso e
  2. non vi è alcuna sovrapposizione nelle due linee dell'arco (che può causare problemi se si utilizzano marcatori o motivi, ecc.). È una linea pulita e continua, sebbene disegnata in due pezzi.

Niente di tutto ciò avrebbe importanza se solo consentissero ai percorsi di testo di accettare forme. Ma penso che stiano evitando quella soluzione poiché gli elementi di forma come il cerchio non hanno tecnicamente un punto di "inizio".

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

Aggiornare:

Se stai usando il percorso come textPathriferimento e desideri che il testo venga visualizzato sul bordo esterno dell'arco, utilizzeresti esattamente lo stesso metodo ma cambi il flag di sweep da 0 a 1 in modo che tratti l'esterno del percorso come superficie anziché all'interno (pensa 1,0a qualcuno seduto al centro e che disegna un cerchio intorno a sé, mentre 1,1come qualcuno che cammina intorno al centro a distanza di raggio e trascina il gesso accanto a loro, se questo è di aiuto). Ecco il codice come sopra ma con la modifica:

<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, grazie per le formule. Naturalmente, i primi due comandi potrebbero essere consolidati.
John Gietzen,

+1 per la chiarezza e la praticità della soluzione in generale, ma soprattutto per "elementi di forma come il cerchio non hanno tecnicamente un punto di partenza" , un modo così chiaro per esprimere il problema.
David John Welsh,

11
Penso che il problema qui non sia quello "oh, sono già lì, non è necessario alcun arco!", Poiché fornendo 1 come bandiera ad arco grande si dice esplicitamente al motore che si desidera che vada oltre. Il vero problema è che ci sono infiniti cerchi che vincolano completamente il passaggio attraverso il tuo punto. Questo spiega perché quando si desidera un arco a 360 *, il motore non sa cosa fare. Il motivo per cui non funziona per 359.999 è che si tratta di un calcolo numericamente instabile, e sebbene tecnicamente ci sia esattamente un cerchio che corrisponde alla richiesta, probabilmente non sarebbe quello che volevi comunque
qbolec

@qbolec - Hai ragione, stavo pensando in termini di arco largo e spazzata spento e l'arco non ha destinazione. O almeno che anche con arco grande e sweep hanno permesso che esistesse un disegno fondamentale che definiva un arco come la curva tra due punti (in modo che con un punto usato due volte, lo vedesse come un singolo punto collassato). la visione dell'arco che potenzialmente disegna 360 cerchi attorno a un punto ha senso, anche se collasserebbe su un cerchio se viene definito un centro, il che solleva la questione di un singolo arco potrebbe fare un cerchio completo se esistesse un centro?
Anthony,

1
"Gli elementi di forma come il cerchio non hanno tecnicamente un punto" iniziale ". Questo non è in realtà vero. Le specifiche SVG specificano assolutamente dove si trova il punto iniziale di tutte le forme. Deve farlo per consentire alle lineette di lavorare sulle forme.
Paul LeBeau

17

In riferimento alla soluzione di Anthony, ecco una funzione per ottenere il percorso:

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

Un approccio totalmente diverso:

Invece di armeggiare con i percorsi per specificare un arco in svg, puoi anche prendere un elemento cerchio e specificare un tratto-dasharray, in pseudo codice:

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

La sua semplicità è il principale vantaggio rispetto al metodo del percorso ad arco multiplo (ad es. Quando si esegue lo script si inserisce un solo valore e il gioco è fatto per qualsiasi lunghezza di arco)

L'arco inizia nel punto più a destra e può essere spostato usando una trasformazione di rotazione.

Nota: Firefox ha un bug strano in cui le rotazioni oltre 90 gradi o più vengono ignorate. Quindi, per iniziare l'arco dall'alto, utilizzare:

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

tenere presente che questa soluzione è applicabile solo nei casi in cui non si utilizza alcun tipo di riempimento e è necessario solo un segmento di arco senza deviazioni. Nel momento in cui vuoi disegnare settori, hai persino bisogno di un nuovo nodo path svg, che altrimenti potrebbe essere gestito in un singolo tag path.
mxfh,

@mxfh no, se si prende la larghezza della corsa due volte il valore di r si ottengono settori perfetti.
mvds

Con stroke-dasharray="0 10cm 5cm 1000cm"è possibile evitare la trasformazione
stenci il

@stenci sì, ma il rovescio della medaglia è che non è possibile avere un parametro nel dasharray per scalare dallo 0% al 100% di riempimento (che era uno dei miei obiettivi)
mvds

5

Adobe Illustrator utilizza curve più bezier come SVG e per i cerchi crea quattro punti. Puoi creare un cerchio con due comandi ad arco ellittico ... ma poi per un cerchio in SVG userei un <circle />:)


"Puoi creare un cerchio con due comandi ad arco ellittico"? cosa intendi? Non puoi semplicemente usarne uno? Almeno facendo passare da (0,0) a (0,01, 0) in modo da renderizzare qualcosa. Per l'utilizzo circle, consultare invece l' aggiornamento nella domanda.
polarità

No, non penso che tu possa usarne solo uno. Hai dimostrato di non poterlo fare e hai un problema simile con gli archi HTML5 Canvas.
Phrogz,

vuoi dire, usando arcper creare un cerchio completo usando l'arco sinistro e l'arco destro? Quindi l'arco non può tracciare un cerchio completo ... per fare ciò arcsono necessari due percorsi
polarità il

2
@ 動靜 能量 Sono necessari due percorsi di arco corretti (o il brutto hack di un arco che percorre la maggior parte del percorso e quindi un comando di percorso ravvicinato per la linea retta fino alla fine).
Phrogz,

1
Illustrator fa schifo. Non puoi fare un cerchio con curve più bezier. Solo qualcosa vicino a un cerchio, ma ancora non un cerchio.
adius il

4

È una buona idea usare il comando a due archi per disegnare un cerchio completo.

di solito, utilizzo l'ellisse o l'elemento cerchio per disegnare un cerchio completo.


5
Una delle principali limitazioni di svg è che gli elementi forma non possono essere utilizzati per i percorsi di testo. Questa è probabilmente la motivazione principale per tutti coloro che visitano questa domanda.
Anthony,

1
@Anthony ora possono. Firefox supporta textPath contro qualsiasi forma. Sospetto che lo faccia anche Chrome.
Robert Longson,

@RobertLongson - Puoi fornire un esempio o collegamenti a un esempio? Curioso come decide dove inizia il percorso del testo e se si trova all'esterno o all'interno (ecc.) Quando viene disegnato senza un punto di partenza tradizionale.
Anthony,

@Anthony Vedi le specifiche SVG 2 ad es. W3.org/TR/SVG2/shapes.html#CircleElement , anche il nuovo attributo lato
Robert Longson

4

Basandomi sulle risposte di Anthony e Anton, ho incorporato la capacità di ruotare il cerchio generato senza influire sul suo aspetto generale. Questo è utile se stai usando il percorso per un'animazione e devi controllare da dove inizia.

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

Questo è esattamente ciò di cui avevo bisogno. Stavo usando il plugin morensock morph per trasformare un percorso circolare in un percorso rettangolare e avevo bisogno di una leggera rotazione per farlo sembrare giusto.
RoboKozo,

3

Per quelli come me che cercavano attributi di un'ellisse per la conversione del percorso:

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

Scritto come una funzione, si presenta così:

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
Questa è un'ottima risposta! Solo una piccola aggiunta: questo percorso è tracciato est, nord, ovest, sud. Se si desidera che il cerchio si comporti esattamente come un <circle>, è necessario cambiare la direzione da Est, Sud, Ovest, Nord: "M" + cx + "," + cy + "m" + (r) + ",0" + "a" + r + "," + r + " 0 1,1 " + (-r * 2) + ",0" + "a" + r + "," + r + " 0 1,1 " + (r * 2) + ",0" Il vantaggio è che stroke-dashoffsete startOffsetora funzionano allo stesso modo.
loominade

2

Queste risposte sono troppo complicate.

Un modo più semplice per farlo senza creare due archi o convertirli in diversi sistemi di coordinate.

Ciò presuppone che l'area della tela abbia larghezza we altezza h.

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

Basta usare la bandiera "arco lungo", quindi la bandiera piena è piena. Quindi rendere gli archi il 99,9999% del cerchio completo. Visivamente è lo stesso. Evita la bandiera sweep semplicemente avviando il cerchio nel punto più a destra del cerchio (un raggio direttamente orizzontale dal centro).


1

Un altro modo sarebbe quello di utilizzare due curve cubiche di Bezier. Questo è per le persone iOS che usano pocketSVG che non riconosce il parametro arco svg.

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

Curva cubica di Bezier

L'ultima serie di coordinate qui (x, y) è dove vuoi che finisca la linea. Gli altri due sono punti di controllo. (x1, y1) è il punto di controllo per l'inizio della curva e (x2, y2) per il punto finale della curva.

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

1

ho creato un jsfiddle per farlo qui:

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

collegamento

tutto quello che devi fare è cambiare l'input di console.log e ottenere il risultato in console


Questo era esattamente quello che stavo cercando. La migliore risposta qui! vota questo ragazzo
Måns Dahlström il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.