Il ne semble pas y avoir de fonction native pour dessiner une forme ovale. Aussi, je ne cherche pas la forme d'oeuf.
Est-il possible de dessiner un ovale avec 2 courbes de Bézier? Quelqu'un d’expertencé avec ça?
Mon but est de dessiner des yeux et en fait, je suis en train d’utiliser des arcs ... Merci d’avance.
Solution
Donc scale () modifie la mise à l'échelle de toutes les formes suivantes . Save () enregistre les paramètres avant et la restauration est utilisée pour restaurer les paramètres afin de dessiner de nouvelles formes sans mise à l'échelle.
Merci à Jani
ctx.save();
ctx.scale(0.75, 1);
ctx.beginPath();
ctx.arc(20, 21, 10, 0, Math.PI*2, false);
ctx.stroke();
ctx.closePath();
ctx.restore();
mises à jour:
Exemple de test JSBin (mis à jour pour tester les réponses des autres à des fins de comparaison)
Original:
Si vous voulez un ovale symétrique, vous pouvez toujours créer un cercle de rayon, puis le redimensionner à la hauteur souhaitée (edit: notez que ceci affectera l'apparence de la largeur du trait - voir la réponse de voulez un contrôle total de l'ellipse, voici un moyen d'utiliser les courbes de Bézier.
<canvas id="thecanvas" width="400" height="400"></canvas>
<script>
var canvas = document.getElementById('thecanvas');
if(canvas.getContext)
{
var ctx = canvas.getContext('2d');
drawEllipse(ctx, 10, 10, 100, 60);
drawEllipseByCenter(ctx, 60,40,20,10);
}
function drawEllipseByCenter(ctx, cx, cy, w, h) {
drawEllipse(ctx, cx - w/2.0, cy - h/2.0, w, h);
}
function drawEllipse(ctx, x, y, w, h) {
var kappa = .5522848,
ox = (w / 2) * kappa, // control point offset horizontal
oy = (h / 2) * kappa, // control point offset vertical
xe = x + w, // x-end
ye = y + h, // y-end
xm = x + w / 2, // x-middle
ym = y + h / 2; // y-middle
ctx.beginPath();
ctx.moveTo(x, ym);
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
//ctx.closePath(); // not used correctly, see comments (use to close off open path)
ctx.stroke();
}
</script>
Voici une version simplifiée des solutions ailleurs. Je dessine un cercle canonique, traduis et redimensionne puis je caresse.
function ellipse(context, cx, cy, rx, ry){
context.save(); // save state
context.beginPath();
context.translate(cx-rx, cy-ry);
context.scale(rx, ry);
context.arc(1, 1, 1, 0, 2 * Math.PI, false);
context.restore(); // restore to original state
context.stroke();
}
L'approche de la courbe de Bézier est idéale pour les ovales simples. Pour plus de contrôle, vous pouvez utiliser une boucle pour dessiner une ellipse avec différentes valeurs pour les rayons x et y (rayons, rayons?).
L'ajout d'un paramètre rotationAngle permet à l'ovale d'être pivoté autour de son centre de n'importe quel angle. Des ovales partiels peuvent être dessinés en modifiant la plage (var i) sur laquelle la boucle s'exécute.
Le rendu de l'ovale de cette manière vous permet de déterminer l'emplacement x, y exact de tous les points de la ligne. Ceci est utile si la position d'autres objets dépend de l'emplacement et de l'orientation de l'ovale.
Voici un exemple de code:
for (var i = 0 * Math.PI; i < 2 * Math.PI; i += 0.01 ) {
xPos = centerX - (radiusX * Math.sin(i)) * Math.sin(rotationAngle * Math.PI) + (radiusY * Math.cos(i)) * Math.cos(rotationAngle * Math.PI);
yPos = centerY + (radiusY * Math.cos(i)) * Math.sin(rotationAngle * Math.PI) + (radiusX * Math.sin(i)) * Math.cos(rotationAngle * Math.PI);
if (i == 0) {
cxt.moveTo(xPos, yPos);
} else {
cxt.lineTo(xPos, yPos);
}
}
Voir un exemple interactif ici: http://www.scienceprimer.com/draw-oval-html5-canvas
Vous avez besoin de 4 courbes de Bézier (et d'un nombre magique) pour reproduire de manière fiable une ellipse. Vois ici:
www.tinaja.com/glib/ellipse4.pdf
Deux beziers ne reproduisent pas exactement une ellipse. Pour le prouver, essayez l'une des deux solutions de Bézier ci-dessus avec une hauteur et une largeur égales - elles devraient idéalement se rapprocher d'un cercle mais ne le feront pas. Ils auront toujours l'air ovale, ce qui prouvera qu'ils ne font pas ce qu'ils sont censés faire.
Voici quelque chose qui devrait fonctionner:
Voici le code:
function ellipse(cx, cy, w, h){
var ctx = canvas.getContext('2d');
ctx.beginPath();
var lx = cx - w/2,
rx = cx + w/2,
ty = cy - h/2,
by = cy + h/2;
var magic = 0.551784;
var xmagic = magic*w/2;
var ymagic = h*magic/2;
ctx.moveTo(cx,ty);
ctx.bezierCurveTo(cx+xmagic,ty,rx,cy-ymagic,rx,cy);
ctx.bezierCurveTo(rx,cy+ymagic,cx+xmagic,by,cx,by);
ctx.bezierCurveTo(cx-xmagic,by,lx,cy+ymagic,lx,cy);
ctx.bezierCurveTo(lx,cy-ymagic,cx-xmagic,ty,cx,ty);
ctx.stroke();
}
Vous pouvez également essayer d'utiliser une mise à l'échelle non uniforme. Vous pouvez fournir une mise à l'échelle X et Y, il vous suffit donc de définir une échelle X ou Y plus grande que l'autre, de tracer un cercle et d'obtenir une ellipse.
J'ai fait une petite adaptation de ce code (présenté en partie par Andrew Staroscik) pour peoplo qui ne veut pas d'une ellipse aussi générale et qui n'a que le plus grand demi-axe et les données d'excentricité de l'ellipse (bon pour le javascript astronomique jouets pour tracer des orbites, par exemple).
Voilà, en vous rappelant qu’on peut adapter les étapes dans i
pour avoir une plus grande précision dans le dessin:
/* draw ellipse
* x0,y0 = center of the ellipse
* a = greater semi-axis
* exc = ellipse excentricity (exc = 0 for circle, 0 < exc < 1 for ellipse, exc > 1 for hyperbole)
*/
function drawEllipse(ctx, x0, y0, a, exc, lineWidth, color)
{
x0 += a * exc;
var r = a * (1 - exc*exc)/(1 + exc),
x = x0 + r,
y = y0;
ctx.beginPath();
ctx.moveTo(x, y);
var i = 0.01 * Math.PI;
var twoPi = 2 * Math.PI;
while (i < twoPi) {
r = a * (1 - exc*exc)/(1 + exc * Math.cos(i));
x = x0 + r * Math.cos(i);
y = y0 + r * Math.sin(i);
ctx.lineTo(x, y);
i += 0.01;
}
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.closePath();
ctx.stroke();
}
Il existe maintenant une fonction ellipse native pour canvas, très similaire à la fonction arc bien que nous ayons maintenant deux valeurs de rayon et une rotation qui soit impressionnante.
ellipse (x, y, rayon X, rayon Y, rotation, angle de départ, angle de fin, sens inverse des aiguilles d'une montre)
ctx.ellipse(100, 100, 10, 15, 0, 0, Math.PI*2);
ctx.fill();
Ne semble fonctionner que dans Chrome actuellement
Ma solution est un peu différente de toutes. Je pense que la plus proche est la réponse la plus votée ci-dessus, mais je pense que cette façon de procéder est un peu plus propre et plus facile à comprendre.
http://jsfiddle.net/jaredwilli/CZeEG/4/
function bezierCurve(centerX, centerY, width, height) {
con.beginPath();
con.moveTo(centerX, centerY - height / 2);
con.bezierCurveTo(
centerX + width / 2, centerY - height / 2,
centerX + width / 2, centerY + height / 2,
centerX, centerY + height / 2
);
con.bezierCurveTo(
centerX - width / 2, centerY + height / 2,
centerX - width / 2, centerY - height / 2,
centerX, centerY - height / 2
);
con.fillStyle = 'white';
con.fill();
con.closePath();
}
Et puis utilisez-le comme ceci:
bezierCurve(x + 60, y + 75, 80, 130);
Il existe quelques exemples d’utilisation dans le violon, ainsi qu’une tentative infructueuse d’en faire un en utilisant quadraticCurveTo.
J'aime la solution des courbes de Bézier ci-dessus. J'ai remarqué que l'échelle affectait également la largeur de trait. Ainsi, si vous essayez de dessiner une ellipse plus large que haute, les "côtés" supérieur et inférieur apparaissent plus minces que les "côtés" gauche et droit ...
un bon exemple serait:
ctx.lineWidth = 4;
ctx.scale(1, 0.5);
ctx.beginPath();
ctx.arc(20, 20, 10, 0, Math.PI * 2, false);
ctx.stroke();
vous remarquerez que la largeur de la ligne au sommet et la vallée de l'ellipse sont deux fois moins larges que celles des sommets gauche et droit.
Chrome et Opera prennent en charge la méthode ellipse pour le contexte canvas 2d, mais IE, Edge, Firefox et Safari ne la prennent pas en charge.
Nous pouvons implémenter la méthode ellipse par JS ou utiliser un polyfill tiers.
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
Exemple d'utilisation:
ctx.ellipse(20, 21, 10, 10, 0, 0, Math.PI*2, true);
Vous pouvez utiliser un fichier canvas-5-polyfill pour fournir une méthode ellipse.
Ou simplement coller du code js pour fournir la méthode ellipse:
if (CanvasRenderingContext2D.prototype.ellipse == undefined) {
CanvasRenderingContext2D.prototype.ellipse = function(x, y, radiusX, radiusY,
rotation, startAngle, endAngle, antiClockwise) {
this.save();
this.translate(x, y);
this.rotate(rotation);
this.scale(radiusX, radiusY);
this.arc(0, 0, 1, startAngle, endAngle, antiClockwise);
this.restore();
}
}
Oui, c'est possible avec deux courbes de Bezier - voici un bref tutoriel/exemple: http://www.williammalone.com/briefs/how-to-draw-ellipse-html5-canvas/
Étant donné que personne n’a proposé une approche utilisant la plus simple quadraticCurveTo
, j’ajoute une solution pour cela. Remplacez simplement les appels bezierCurveTo
dans le answer de @ Steve par ceci:
ctx.quadraticCurveTo(x,y,xm,y);
ctx.quadraticCurveTo(xe,y,xe,ym);
ctx.quadraticCurveTo(xe,ye,xm,ye);
ctx.quadraticCurveTo(x,ye,x,ym);
Vous pouvez également supprimer la closePath
. L'ovale est légèrement différent cependant.
Avec cela, vous pouvez même dessiner des segments d'une ellipse:
function ellipse(color, lineWidth, x, y, stretchX, stretchY, startAngle, endAngle) {
for (var angle = startAngle; angle < endAngle; angle += Math.PI / 180) {
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(x + Math.cos(angle) * stretchX, y + Math.sin(angle) * stretchY)
ctx.lineWidth = lineWidth
ctx.strokeStyle = color
ctx.stroke()
ctx.closePath()
}
}
C’est une autre façon de créer une forme semblable à une ellipse, bien qu’elle utilise la fonction "fillRect ()", elle peut être utilisée pour modifier les arguments de la fonction fillRect ().
<!DOCTYPE html>
<html lang="en">
<head>
<title>Sine and cosine functions</title>
</head>
<body>
<canvas id="trigCan" width="400" height="400"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("trigCan"), ctx = canvas.getContext('2d');
for (var i = 0; i < 360; i++) {
var x = Math.sin(i), y = Math.cos(i);
ctx.stroke();
ctx.fillRect(50 * 2 * x * 2 / 5 + 200, 40 * 2 * y / 4 + 200, 10, 10, true);
}
</script>
</body>
</html>
Voici une fonction que j'ai écrite qui utilise les mêmes valeurs que l'arc d'ellipse en SVG. X1 et Y1 sont les dernières coordonnées, X2 et Y2 sont les coordonnées de fin, le rayon est une valeur numérique et dans le sens des aiguilles d'une montre, une valeur booléenne. Cela suppose également que votre contexte de toile a déjà été défini.
function ellipse(x1, y1, x2, y2, radius, clockwise) {
var cBx = (x1 + x2) / 2; //get point between xy1 and xy2
var cBy = (y1 + y2) / 2;
var aB = Math.atan2(y1 - y2, x1 - x2); //get angle to bulge point in radians
if (clockwise) { aB += (90 * (Math.PI / 180)); }
else { aB -= (90 * (Math.PI / 180)); }
var op_side = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) / 2;
var adj_side = Math.sqrt(Math.pow(radius, 2) - Math.pow(op_side, 2));
if (isNaN(adj_side)) {
adj_side = Math.sqrt(Math.pow(op_side, 2) - Math.pow(radius, 2));
}
var Cx = cBx + (adj_side * Math.cos(aB));
var Cy = cBy + (adj_side * Math.sin(aB));
var startA = Math.atan2(y1 - Cy, x1 - Cx); //get start/end angles in radians
var endA = Math.atan2(y2 - Cy, x2 - Cx);
var mid = (startA + endA) / 2;
var Mx = Cx + (radius * Math.cos(mid));
var My = Cy + (radius * Math.sin(mid));
context.arc(Cx, Cy, radius, startA, endA, clockwise);
}
Si vous voulez que l'ellipse s'intègre parfaitement à l'intérieur d'un rectangle, procédez comme suit:
function ellipse(canvasContext, x, y, width, height){
var z = canvasContext, X = Math.round(x), Y = Math.round(y), wd = Math.round(width), ht = Math.round(height), h6 = Math.round(ht/6);
var y2 = Math.round(Y+ht/2), xw = X+wd, ym = Y-h6, yp = Y+ht+h6, cs = cards, c = this.card;
z.beginPath(); z.moveTo(X, y2); z.bezierCurveTo(X, ym, xw, ym, xw, y2); z.bezierCurveTo(xw, yp, X, yp, X, y2); z.fill(); z.stroke();
return z;
}
Assurez-vous que votre canvasContext.fillStyle = 'rgba(0,0,0,0)';
pour aucun remplissage avec cette conception.