Dessin de cercle avec la trajectoire de l'arc de SVG


147

Question courte: en utilisant le chemin SVG, nous pouvons dessiner 99,99% d'un cercle et cela apparaît, mais quand il s'agit de 99,99999999% d'un cercle, le cercle n'apparaîtra pas. Comment y remédier?

Le chemin SVG suivant peut dessiner 99,99% d'un cercle: (essayez-le sur http://jsfiddle.net/DFhUF/1381/ et voyez si vous voyez 4 arcs ou seulement 2 arcs, mais notez que si c'est IE, c'est rendu en VML, pas SVG, mais ont le même problème)

M 100 100 a 50 50 0 1 0 0.00001 0

Mais quand il s'agit de 99,99999999% d'un cercle, alors rien ne s'affiche du tout?

M 100 100 a 50 50 0 1 0 0.00000001 0    

Et c'est pareil avec 100% d'un cercle (c'est toujours un arc, n'est-ce pas, juste un arc très complet)

M 100 100 a 50 50 0 1 0 0 0 

Comment cela peut-il être réglé? La raison en est que j'utilise une fonction pour dessiner un pourcentage d'un arc, et si j'ai besoin d'un "cas particulier" un arc de 99,9999% ou 100% pour utiliser la fonction de cercle, ce serait un peu idiot.

Encore une fois, un cas de test sur jsfiddle utilisant RaphaelJS est à http://jsfiddle.net/DFhUF/1381/
(et s'il s'agit de VML sur IE 8, même le deuxième cercle ne s'affiche pas ... vous devez le changer en 0,01)


Mettre à jour:

C'est parce que je suis en train de rendre un arc pour un score dans notre système, donc 3,3 points donnent 1/3 d'un cercle. 0,5 correspond à un demi-cercle et 9,9 points à 99% d'un cercle. Mais que faire s'il y a des scores de 9,99 dans notre système? Dois-je vérifier s'il est proche de 99,999% d'un cercle et utiliser une arcfonction ou une circlefonction en conséquence? Alors qu'en est-il d'un score de 9,9987? Lequel utiliser? Il est ridicule d'avoir besoin de savoir quel type de scores correspondra à un "cercle trop complet" et basculera vers une fonction de cercle, et quand il s'agit "d'un certain 99,9%" d'un cercle ou d'un score de 9,9987, alors utilisez la fonction arc .


@minitech bien sûr ça marche ... c'est alors 99% d'un cercle (en gros). Le cas est qu'il peut dessiner 98%, 99%, 99,99% d'un cercle, mais pas 99,9999999% ou 100%
nonopolarité

1
Ces deux liens mènent à la même chose et cela fonctionne bien dans Safari.
Mark Bessey

à droite, même lien, je veux juste que les gens voient le cas de test plus tôt donc j'ajoute le lien au début de la question. Bon safari le fera, comme c'est sympa ... Chrome et Firefox ne le feront pas ... un peu étrange car Safari et Chrome sont tous les deux Webkit ... mais le moteur SVG dépend-il de Webkit?
nonopolarité

@Marcin a l'air bien comment? voyez-vous 4 arcs ou 2 arcs? avez-vous même regardé le code?
nonopolarité

2
un jsfiddle mis à jour avec Raphael inclus en tant que bibliothèque, pour contourner une erreur d'origine croisée lors du chargement de raphael.js: jsfiddle.net/DFhUF/1381
ericsoco

Réponses:


35

Idem pour l'arc de XAML. Fermez simplement l'arc à 99,99% avec un Zet vous avez un cercle!


alors pouvez-vous fermer le troisième cas dans l'exemple jsfiddle et le faire fonctionner?
nonopolarité

en abaissant la valeur à 0,0001, oui. le problème semble lié à l'implémentation de WebKit dans différents navigateurs, et non à SVG lui-même. Mais c'est ainsi que vous créez un cercle dans le composant Arc SVG / XAML.
Todd Main

Ahhh, tricher, toujours la meilleure solution. Encore faut-il gérer le cas à 100%, mais simplement en «arrondissant» à 99,999% plutôt qu'avec une branche de code entièrement séparée.
SimonR

Une démo? Cette réponse ne remplit pas
Medet Tleukabiluly

@MedetTleukabiluly vous avez l'arc ci-dessus dans la question, ajoutez un zà la fin et c'est fait. Vous devrez peut-être supprimer des zéros, par exemple dans le dernier Chrome, j'ai dû écrire 0.0001pour que cela fonctionne. Si vous cherchez où placer la chaîne de chemin, regardez Chemins sur MDN .
TWiStErRob

416

Je sais que c'est un peu tard dans le jeu, mais je me suis souvenu de cette question quand c'était nouveau et j'avais un dillemma similaire, et j'ai accidentellement trouvé la «bonne» solution, si quelqu'un en cherche encore une:

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

En d'autres termes, ceci:

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

peut être réalisé comme un chemin avec ceci:

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

L'astuce consiste à avoir deux arcs, le second reprenant là où le premier s'était arrêté et utilisant le diamètre négatif pour revenir au point de départ de l'arc d'origine.

La raison pour laquelle cela ne peut pas être fait comme un cercle complet dans un arc (et je ne fais que spéculer) est que vous lui diriez de dessiner un arc de lui-même (disons 150,150) à lui-même (150,150), ce qu'il rend comme "oh, je suis déjà là, pas d'arc nécessaire!".

Les avantages de la solution que je propose sont:

  1. il est facile de traduire directement d'un cercle en chemin, et
  2. il n'y a pas de chevauchement dans les deux lignes d'arc (ce qui peut poser des problèmes si vous utilisez des marqueurs ou des motifs, etc.). C'est une ligne continue propre, bien que dessinée en deux morceaux.

Rien de tout cela n'aurait d'importance s'ils autorisaient simplement les chemins de texte à accepter des formes. Mais je pense qu'ils évitent cette solution car les éléments de forme comme le cercle n'ont techniquement pas de point de «départ».

Démo jsfiddle: http://jsfiddle.net/crazytonyi/mNt2g/

Mettre à jour:

Si vous utilisez le tracé pour une textPathréférence et que vous souhaitez que le texte soit rendu sur le bord extérieur de l'arc, vous utiliseriez exactement la même méthode, mais changez l'indicateur de balayage de 0 à 1 afin qu'il traite l'extérieur du chemin comme surface au lieu de l'intérieur (pensez 1,0à quelqu'un assis au centre et à dessiner un cercle autour d'eux, tandis 1,1que quelqu'un marchant autour du centre à distance de rayon et traînant sa craie à côté d'eux, si cela peut aider). Voici le code comme ci-dessus mais avec le changement:

<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, merci pour les formules. Bien entendu, les deux premières commandes pourraient être consolidées.
John Gietzen

+1 pour la clarté et l'aspect pratique de la solution en général, mais surtout pour l'aspect «les éléments de forme comme le cercle n'ont techniquement pas de point de« départ »» - une manière si claire d'exprimer le problème.
David John Welsh

11
Je pense que le problème ici n'est pas que "oh, je suis déjà là, pas d'arc nécessaire!", Car en fournissant 1 comme grand-arc-flag vous dites explicitement au moteur que vous voulez qu'il aille plus loin. Le vrai problème est qu'il existe une infinité de cercles qui remplissent les contraintes de franchissement de votre point. Cela explique pourquoi lorsque vous voulez un arc à 360 *, le moteur ne sait pas quoi faire. La raison pour laquelle cela ne fonctionne pas pour 359.999 est qu'il s'agit d'un calcul numériquement instable, et bien qu'il y ait techniquement exactement un cercle qui correspond à la demande, ce ne serait probablement pas celui que vous vouliez de toute façon
qbolec

@qbolec - Vous avez raison, je pensais en termes de grand arc et de balayage et que l'arc n'avait pas de destination. Ou à tout le moins que même avec un grand arc et un balayage activé, il y avait une conception fondamentale qui définissait un arc comme la courbe entre deux points (de sorte qu'avec un point utilisé deux fois, il le voyait comme un point unique réduit). la vision de l'arc dessinant potentiellement 360 cercles autour d'un point a du sens, même si elle s'effondrerait en un seul cercle si un centre est défini, ce qui soulève la question de savoir si un seul arc pourrait-il faire un cercle complet si un centre existait?
Anthony

1
"Les éléments de forme comme le cercle n'ont techniquement pas de" point de départ ". Ce n'est pas vraiment vrai. La spécification SVG spécifie absolument où se trouve le point de départ de toutes les formes. Il doit le faire pour que les tableaux de tirets fonctionnent sur les formes.
Paul LeBeau

17

En référence à la solution d'Anthony, voici une fonction pour obtenir le chemin:

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

Une approche totalement différente:

Au lieu de jouer avec les chemins pour spécifier un arc en svg, vous pouvez également prendre un élément de cercle et spécifier un stroke-dasharray, en pseudo code:

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

Sa simplicité est le principal avantage par rapport à la méthode de chemins d'arc multiples (par exemple, lors du script, vous ne branchez qu'une seule valeur et vous avez terminé pour n'importe quelle longueur d'arc)

L'arc commence au point le plus à droite et peut être déplacé à l'aide d'une transformation de rotation.

Remarque: Firefox a un bug étrange où les rotations de plus de 90 degrés ou plus sont ignorées. Donc, pour démarrer l'arc par le haut, utilisez:

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

sachez que cette solution n'est applicable que dans les cas où vous n'utilisez aucun type de remplissage et vous n'avez besoin que d'un segment d'arc sans écarts. Au moment où vous souhaitez dessiner des secteurs, vous avez même besoin d'un nouveau nœud de chemin svg, qui pourrait autrement être géré dans une seule balise de chemin.
mxfh

@mxfh pas vraiment, si vous prenez une largeur de trait deux fois la valeur de r, vous obtenez des secteurs parfaits.
mvds

Avec stroke-dasharray="0 10cm 5cm 1000cm"il est possible d'éviter la transformation
stenci

@stenci oui, mais l'inconvénient est que vous ne pouvez pas avoir un paramètre dans le dasharray pour passer de 0% à 100% de remplissage (ce qui était l'un de mes objectifs)
mvds

5

Adobe Illustrator utilise des courbes de Bézier comme SVG et pour les cercles, il crée quatre points. Vous pouvez créer un cercle avec deux commandes d'arc elliptique ... mais alors pour un cercle en SVG, j'utiliserais un <circle />:)


"Vous pouvez créer un cercle avec deux commandes d'arc elliptique"? Que voulez-vous dire? Vous ne pouvez pas en utiliser un? Au moins en faisant passer de (0,0) à (0,01, 0) pour qu'il rende quelque chose. Pour l'utilisation circle, veuillez plutôt consulter la mise à jour dans la question.
nonopolarité

Non, je ne pense pas que vous puissiez en utiliser un seul. Vous avez montré que vous ne pouvez pas, et vous avez un problème similaire avec les arcs HTML5 Canvas.
Phrogz

vous voulez dire, en utilisant arcpour créer un cercle complet en utilisant l'arc gauche et l'arc droit? Ainsi l'arc ne peut pas dessiner un cercle complet ... pour ce faire, deux arcchemins sont nécessaires
nonopolarité

2
@ 動靜 能量 Correct, deux chemins d'arc sont nécessaires (ou le vilain hack d'un arc allant presque tout au long du chemin, puis une commande closepath pour aller en ligne droite jusqu'à la fin).
Phrogz

1
Illustrator est nul. Vous ne pouvez pas faire de cercle avec des courbes de Bézier. Seulement quelque chose proche d'un cercle, mais toujours pas un cercle.
adius

4

C'est une bonne idée d'utiliser la commande deux arc pour dessiner un cercle complet.

généralement, j'utilise une ellipse ou un élément de cercle pour dessiner un cercle complet.


5
Une limitation majeure de svg est que les éléments de forme ne peuvent pas être utilisés pour les chemins de texte. C'est probablement la principale motivation de tous ceux qui consultent cette question.
Anthony

1
@Anthony, ils peuvent maintenant. Firefox prend en charge textPath contre n'importe quelle forme. Je soupçonne que Chrome le fait aussi.
Robert Longson

@RobertLongson - Pouvez-vous fournir un exemple ou des liens vers un exemple? Curieux de savoir comment il décide où commence le chemin du texte et s'il se trouve à l'extérieur ou à l'intérieur (etc.) lorsqu'il est dessiné sans point de départ traditionnel.
Anthony

@Anthony Voir la spécification SVG 2, par exemple w3.org/TR/SVG2/shapes.html#CircleElement , également le nouvel attribut latéral
Robert Longson

4

En me basant sur les réponses d'Anthony et Anton, j'ai intégré la possibilité de faire pivoter le cercle généré sans affecter son apparence générale. Ceci est utile si vous utilisez le chemin pour une animation et que vous devez contrôler son point de départ.

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

C'est exactement ce dont j'avais besoin. J'utilisais le plugin greensock morph pour transformer un chemin circulaire en un chemin rectangulaire et j'avais besoin d'une légère rotation pour qu'il soit parfait.
RoboKozo

3

Pour ceux comme moi qui recherchaient des attributs d'ellipse en conversion de chemin:

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

Ecrit comme une fonction, il ressemble à ceci:

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
C'est une excellente réponse! Juste un petit ajout: ce chemin est tracé Est, Nord, Ouest, Sud. Si vous voulez que le cercle se comporte exactement comme a <circle>, vous devez changer la direction est, sud, ouest, 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" l'avantage est que stroke-dashoffsetet startOffsetmaintenant travailler de la même manière.
loominade

2

Ces réponses sont beaucoup trop compliquées.

Une façon plus simple de le faire sans créer deux arcs ou convertir en différents systèmes de coordonnées.

Cela suppose que votre zone de canevas a une largeur wet une hauteur h.

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

Utilisez simplement le drapeau "long arc", de sorte que le drapeau complet soit rempli. Faites ensuite les arcs à 99,9999% du cercle complet. Visuellement, c'est pareil. Évitez le drapeau de balayage en commençant simplement le cercle au point le plus à droite du cercle (un rayon directement horizontal du centre).


1

Une autre façon serait d'utiliser deux courbes de Bézier cubiques. C'est pour les gens iOS qui utilisent pocketSVG qui ne reconnaît pas le paramètre d'arc svg.

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

Courbe de Bézier cubique

Le dernier ensemble de coordonnées ici (x, y) est l'endroit où vous voulez que la ligne se termine. Les deux autres sont des points de contrôle. (x1, y1) est le point de contrôle pour le début de votre courbe, et (x2, y2) pour le point final de votre courbe.

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

1

j'ai fait un jsfiddle pour le faire ici:

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

lien

tout ce que vous avez à faire est de changer l'entrée de console.log et d'obtenir le résultat dans console


C'était exactement ce que je recherchais. Meilleure réponse ici! voter pour ce gars
Måns Dahlström
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.