web-dev-qa-db-fra.com

Dessin en cercle avec chemin d'arc SVG

Petite question: en utilisant le chemin SVG, nous pouvons dessiner 99,99% d'un cercle et il apparaît, mais s'il est à 99,99999999% d'un cercle, le cercle n'apparaîtra pas. Comment peut-il être corrigé?

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, il est rendu en VML, pas en SVG, mais a le même problème)

M 100 100 a 50 50 0 1 0 0.00001 0

Mais quand il est 99,99999999% d'un cercle, alors rien ne montrera du tout?

M 100 100 a 50 50 0 1 0 0.00000001 0    

Et c'est la même chose 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 corrigé? La raison est que j’utilise une fonction pour dessiner un pourcentage d’arc, et si j’ai besoin de "cas spécial", un arc à 99,9999% ou 100% pour utiliser la fonction cercle, ce serait un peu ridicule.

Encore une fois, un scénario de test sur jsfiddle utilisant RaphaelJS se trouve à http://jsfiddle.net/DFhUF/1381/
(et si VML est sur IE 8, même le deuxième cercle ne s'affichera pas ... vous devez le changer en 0.01)


Mise à jour:

Ceci est dû au fait que je suis en train de générer un arc de score dans notre système. 3,3 points forment ainsi 1/3 de cercle. 0,5 correspond à un demi-cercle et 9,9 points à 99% d'un cercle. Mais que se passe-t-il 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 la fonction arc ou circle en conséquence? Alors que diriez-vous d'un score de 9,9987? Lequel utiliser? Il est ridicule de savoir quel type de partition correspond à un "cercle trop complet" et à passer à une fonction de cercle. Lorsqu'il s'agit "d'un certain 99,9%" d'un cercle ou d'un score de 9,9987, utilisez la fonction arc .

136

Même chose pour l'arc de XAML. Fermez l’arc à 99,99% avec un Z et vous avez un cercle!

33
Todd Main

Je sais qu'il est un peu tard dans le jeu, mais je me suis souvenu de cette question depuis sa création et que j'avais un dillemme similaire, et j'ai accidentellement trouvé la "bonne" solution, si quelqu'un en cherche toujours un:

<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'est 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 parce que vous lui diriez de dessiner un arc de lui-même (disons 150 150) vers lui-même (150 150), ce qu'il rend comme "oh, je suis déjà là, aucun arc n'est nécessaire!".

Les avantages de la solution que je propose sont les suivants:

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

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 de jsfiddle: http://jsfiddle.net/crazytonyi/mNt2g/

Mise à jour:

Si vous utilisez le chemin d'accès pour une référence textPath et que vous souhaitez que le texte soit restitué sur le bord extérieur de l'arc, utilisez la même méthode mais changez l'indicateur de balayage de 0 à 1 de sorte que il traite l'extérieur du chemin comme la surface au lieu de l'intérieur (pensez à 1,0 comme quelqu'un assis au centre et dessinant un cercle autour d’eux, tandis que 1,1 comme quelqu'un marchant autour du centre à un rayon et traînant sa craie à côté d’eux, si cela peut vous 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
    "
/>
387
Anthony

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';
}
15
Anton Matiashevich

Une approche totalement différente:

Au lieu de manipuler les chemins pour spécifier un arc dans svg, vous pouvez également prendre un élément circle 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 de la méthode des chemins d’arc multiples (par exemple, lors de la création de scripts, vous ne connectez qu’une 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 dans lequel les rotations supérieures à 90 degrés ou plus sont ignorées. Donc, pour commencer l'arc par le haut, utilisez:

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

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

5
Phrogz

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 d'une animation et que vous devez contrôler où il commence.

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

Il est judicieux d'utiliser deux commandes d'arc pour tracer un cercle complet.

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

3
cuixiping

Ecrit en tant que 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
Harry Stevens

Ces réponses sont beaucoup trop compliquées.

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

Cela suppose que votre zone de travail a la largeur w et la 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 "arc long" pour que le drapeau complet soit rempli. Ensuite, faites les arcs à 99,9999% du cercle complet. Visuellement, c'est pareil. Évitez le drapeau de balayage en commençant simplement par le cercle au point le plus à droite du cercle (un rayon directement horizontal du centre).

Pour ceux qui, comme moi, recherchent une ellipse comme attributs de 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`
1
yogi

Une autre solution consisterait à utiliser deux courbes de Bézier cubique. C'est pour les gens iOS utilisant pocketSVG qui ne reconnaît pas le paramètre d'arc svg.

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

Cubic Bezier curve

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

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 modifier l’entrée de console.log et d’obtenir le résultat dans la console.

0
javad bat