260 votes

Comment calculer le chemin SVG d'un arc (de cercle)

Étant donné un cercle centré à (200,200), de rayon 25, comment puis-je dessiner un arc de 270 degrés à 135 degrés et un arc qui va de 270 à 45 degrés ?

0 degré signifie qu'il se trouve sur l'axe des x (le côté droit) (ce qui signifie qu'il s'agit de la position 3 heures) 270 degrés signifie qu'il s'agit de la position 12 heures, et 90 degrés signifie qu'il s'agit de la position 6 heures.

Plus généralement, quelle est la trajectoire d'un arc pour une partie de cercle avec

x, y, r, d1, d2, direction

signification

center (x,y), radius r, degree_start, degree_end, direction

505voto

opsb Points 6860

En complément de l'excellente réponse de @wdebeaum, voici une méthode pour générer une trajectoire en arc de cercle :

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

à utiliser

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 180));

et dans votre html

<path id="arc1" fill="none" stroke="#446688" stroke-width="20" />

Démonstration en direct

9 votes

C'est super ! Notez que le arcSweep contrôle en fait la variable large-arc-flag svg Un paramètre. Dans le code ci-dessus, la valeur du paramètre sweep-flag est toujours égal à zéro. arcSweep devrait probablement être renommé en quelque chose comme longArc .

0 votes

Merci @PocketLogic, j'ai (enfin) mis à jour selon votre suggestion.

4 votes

Vraiment utile, merci. La seule chose que j'ai trouvée est que la logique de largeArc ne fonctionne pas si vous utilisez des angles négatifs. Cela fonctionne de -360 à +360 : jsbin.com/kopisonewi/2/edit?html,js,output

155voto

wdebeaum Points 2116

Vous voulez utiliser le elliptique A commande rc . Malheureusement pour vous, cela nécessite de spécifier les coordonnées cartésiennes (x, y) des points de départ et d'arrivée plutôt que les coordonnées polaires (rayon, angle) dont vous disposez, ce qui vous oblige à faire quelques calculs. Voici une fonction JavaScript qui devrait fonctionner (bien que je ne l'aie pas testée) et qui, je l'espère, est assez explicite :

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = angleInDegrees * Math.PI / 180.0;
  var x = centerX + radius * Math.cos(angleInRadians);
  var y = centerY + radius * Math.sin(angleInRadians);
  return [x,y];
}

Les angles correspondant aux positions de l'horloge dépendent du système de coordonnées ; il suffit de permuter et/ou de nier les termes sin/cos si nécessaire.

La commande arc a ces paramètres :

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y

Pour votre premier exemple :

rx = ry =25 et x-axis-rotation =0, puisque vous voulez un cercle et non une ellipse. Vous pouvez calculer à la fois les coordonnées de départ (que vous devriez utiliser) et les coordonnées de fin. M ove to) et les coordonnées finales (x, y) en utilisant la fonction ci-dessus, ce qui donne (200, 175) et environ (182,322, 217,678), respectivement. Compte tenu de ces contraintes jusqu'à présent, il y a en fait quatre arcs qui pourraient être dessinés, donc les deux drapeaux sélectionnent l'un d'entre eux. Je suppose que vous voulez probablement dessiner un petit arc (c'est-à-dire large-arc-flag =0), dans le sens d'un angle décroissant (signifiant sweep-flag =0). Au total, le chemin SVG est :

M 200 175 A 25 25 0 0 0 182.322 217.678

Pour le deuxième exemple (en supposant que vous voulez dire aller dans la même direction, et donc un grand arc), le chemin SVG est :

M 200 175 A 25 25 0 1 0 217.678 217.678

Encore une fois, je ne les ai pas testés.

(edit 2016-06-01) Si, comme @clocksmith, vous vous demandez pourquoi ils ont choisi cette API, jetez un coup d'œil à l' notes d'application . Ils décrivent deux paramétrisations possibles de l'arc, la "paramétrisation du point final" (celle qu'ils ont choisie) et la "paramétrisation du centre" (qui ressemble à celle utilisée dans la question). Dans la description de la "paramétrisation du point final", ils disent :

L'un des avantages de la paramétrisation du point d'arrivée est qu'elle permet une syntaxe de parcours cohérente dans laquelle toutes les commandes de parcours se terminent par les coordonnées du nouveau "point courant".

En fait, il s'agit d'un effet secondaire des arcs qui sont considérés comme faisant partie d'un chemin plus large plutôt que comme un objet distinct. Je suppose que si votre moteur de rendu SVG est incomplet, il pourrait simplement sauter tous les composants de chemin qu'il ne sait pas comment rendre, tant qu'il sait combien d'arguments ils prennent. Ou peut-être que cela permet un rendu parallèle de différentes parties d'un chemin avec de nombreux composants. Ou peut-être ont-ils fait cela pour s'assurer que les erreurs d'arrondi ne s'accumulent pas sur la longueur d'un chemin complexe.

Les notes de mise en œuvre sont également utiles pour la question initiale, car elles contiennent un pseudocode plus mathématique pour la conversion entre les deux paramétrages (ce que je n'avais pas réalisé lorsque j'ai écrit cette réponse pour la première fois).

1 votes

Ça a l'air bien, j'essaierai la prochaine fois. le fait que si c'est "plus" de l'arc (quand l'arc est plus de la moitié du cercle) et que le large-arc-flag doit être basculé de 0 à 1 pourrait introduire un bug.

0 votes

Ugh, pourquoi est-ce l'api pour les arcs svg ?

31voto

Ahtenus Points 136

J'ai légèrement modifié la réponse d'opsb et réalisé en remplissage de support pour le secteur du cercle. http://codepen.io/anon/pen/AkoGx

JS

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 arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, arcSweep, 0, end.x, end.y,
        "L", x,y,
        "L", start.x, start.y
    ].join(" ");

    return d;       
}

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 220));

HTML

<svg>
  <path id="arc1" fill="orange" stroke="#446688" stroke-width="0" />
</svg>

9 votes

Le lien vers le codepen ne semble pas fonctionner pour moi (Chrome)

0 votes

L'arc est dessiné en dehors des limites du SVG. Augmentez la hauteur du SVG et modifiez centerX , centerY à 100 par exemple.

0 votes

Vous pouvez également définir un cadre d'affichage de manière explicite dans l'élément svg parent. Par exemple viewBox="0 0 500 500"

23voto

SumNeuron Points 1075

C'est une vieille question, mais j'ai trouvé le code utile et m'a épargné trois minutes de réflexion :) J'ajoute donc une petite extension à @opsb Réponse de la Commission .

Si vous souhaitez convertir cet arc en une tranche (pour permettre le remplissage), nous pouvons modifier légèrement le code :

function describeArc(x, y, radius, spread, startAngle, endAngle){
    var innerStart = polarToCartesian(x, y, radius, endAngle);
    var innerEnd = polarToCartesian(x, y, radius, startAngle);
    var outerStart = polarToCartesian(x, y, radius + spread, endAngle);
    var outerEnd = polarToCartesian(x, y, radius + spread, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", outerStart.x, outerStart.y,
        "A", radius + spread, radius + spread, 0, largeArcFlag, 0, outerEnd.x, outerEnd.y,
        "L", innerEnd.x, innerEnd.y, 
        "A", radius, radius, 0, largeArcFlag, 1, innerStart.x, innerStart.y, 
        "L", outerStart.x, outerStart.y, "Z"
    ].join(" ");

    return d;
}

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

var path = describeArc(150, 150, 50, 30, 0, 50)
document.getElementById("p").innerHTML = path
document.getElementById("path").setAttribute('d',path)

<p id="p">
</p>
<svg width="300" height="300" style="border:1px gray solid">
  <path id="path" fill="blue" stroke="cyan"></path>
</svg>

et voilà !

17voto

Maciek Jurczyk Points 305

Note pour ceux qui cherchent des réponses (que j'étais aussi) - si l'utilisation de l'arc n'est pas obligatoire une solution beaucoup plus simple pour dessiner un cercle partiel est d'utiliser la fonction stroke-dasharray de SVG <circle> .

Divisez le tableau de bord en deux éléments, et mettez leur portée à l'échelle de l'angle souhaité. L'angle de départ peut être ajusté en utilisant stroke-dashoffset .

Pas un seul cosinus en vue.

Exemple complet avec explications : https://codepen.io/mjurczyk/pen/wvBKOvP

0 votes

Merci pour cette réponse !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X