No parece haber una función nativa para dibujar una forma ovalada. Además, no estoy buscando la forma de huevo.
¿Es posible dibujar un óvalo con 2 curvas Bézier? ¿Alguien experimentó con eso?
Mi propósito es dibujar algunos ojos y en realidad solo estoy usando arcos. Gracias por adelantado.
Solución
Entonces scale () cambia la escala para todas las siguientes formas. Guardar () guarda la configuración antes y restaurar se usa para restaurar la configuración para dibujar nuevas formas sin escalar.
Gracias a 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();
javascript
html
canvas
Tammo
fuente
fuente
restore()
antesstroke()
, no distorsionará la línea, como mencionó Johnathan Hebert en un comentario a la respuesta de Steve Tranby a continuación. Además, este es un uso innecesario e incorrecto declosePath()
. Un método tan incomprendido ... stackoverflow.com/questions/10807230/…Respuestas:
actualizaciones:
Ejemplo de prueba JSBin (actualizado para probar las respuestas de otros para compararlas)
Original:
Si desea un óvalo simétrico, siempre puede crear un círculo de ancho de radio y luego escalarlo a la altura que desee ( editar: observe que esto afectará la apariencia del ancho del trazo; consulte la respuesta de acdameli), pero si desea un control total de la elipse aquí hay una forma de usar curvas 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>
fuente
scale
que distorsionará los trazos (consulte la respuesta de acdameli).Aquí hay una versión simplificada de soluciones en otros lugares. Dibujo un círculo canónico, traduzco y escalo y luego trazo.
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(); }
fuente
context
hacer el trabajo pesado conscale
.Ahora hay una función de elipse nativa para lienzo, muy similar a la función de arco, aunque ahora tenemos dos valores de radio y una rotación que es increíble.
Demo en vivo
ctx.ellipse(100, 100, 10, 15, 0, 0, Math.PI*2); ctx.fill();
Parece que solo funciona en Chrome actualmente
fuente
El enfoque de la curva de Bézier es ideal para óvalos simples. Para mayor control, puede usar un bucle para dibujar una elipse con diferentes valores para los radios xey (radios, radios?).
Agregar un parámetro de ángulo de rotación permite que el óvalo se gire alrededor de su centro en cualquier ángulo. Se pueden dibujar óvalos parciales cambiando el rango (var i) sobre el que se ejecuta el bucle.
Representar el óvalo de esta manera le permite determinar la ubicación exacta x, y de todos los puntos en la línea. Esto es útil si la posición de otros objetos depende de la ubicación y orientación del óvalo.
A continuación, se muestra un ejemplo del código:
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); } }
Vea un ejemplo interactivo aquí: http://www.scienceprimer.com/draw-oval-html5-canvas
fuente
Necesita 4 curvas Bézier (y un número mágico) para reproducir una elipse de manera confiable. Mira aquí:
www.tinaja.com/glib/ellipse4.pdf
Dos béziers no reproducen con precisión una elipse. Para probar esto, pruebe algunas de las 2 soluciones Bézier anteriores con la misma altura y ancho; idealmente deberían aproximarse a un círculo pero no lo harán. Todavía se verán ovalados, lo que demuestra que no están haciendo lo que se supone que deben hacer.
Aquí hay algo que debería funcionar:
http://jsfiddle.net/BsPsj/
Aquí está el código:
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(); }
fuente
También puede intentar usar una escala no uniforme. Puede proporcionar escalas X e Y, así que simplemente configure la escala X o Y más grande que la otra, y dibuje un círculo, y tendrá una elipse.
fuente
Hice una pequeña adaptación de este código (presentado parcialmente por Andrew Staroscik) para personas que no quieren una elipse tan general y que solo tienen el semieje mayor y los datos de excentricidad de la elipse (bueno para los juguetes astronómicos de JavaScript para trazar órbitas , por ejemplo).
Aquí tienes, recordando que se pueden adaptar los pasos
i
para tener una mayor precisión en el dibujo:/* 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(); }
fuente
Mi solución es un poco diferente a todas estas. Sin embargo, creo que la respuesta más cercana es la más votada, pero creo que de esta manera es un poco más limpia y más fácil de comprender.
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(); }
Y luego úsalo así:
bezierCurve(x + 60, y + 75, 80, 130);
Hay un par de ejemplos de uso en el violín, junto con un intento fallido de hacer uno usando quadraticCurveTo.
fuente
Me gusta la solución de curvas de Bezier anterior. Noté que la escala también afecta el ancho de la línea, por lo que si está tratando de dibujar una elipse que sea más ancha que alta, sus "lados" superior e inferior aparecerán más delgados que sus "lados" izquierdo y derecho ...
un buen ejemplo sería:
ctx.lineWidth = 4; ctx.scale(1, 0.5); ctx.beginPath(); ctx.arc(20, 20, 10, 0, Math.PI * 2, false); ctx.stroke();
debe notar que el ancho de la línea en el pico y el valle de la elipse es la mitad de ancho que en los ápices izquierdo y derecho (¿ápices?).
fuente
ctx.scale(0.5, 1);
Sí, es posible con dos curvas Bézier; aquí hay un breve tutorial / ejemplo: http://www.williammalone.com/briefs/how-to-draw-ellipse-html5-canvas/
fuente
Chrome y Opera admiten el método de elipse para el contexto de lienzo 2d, pero IE, Edge, Firefox y Safari no lo admiten.
Podemos implementar el método elipse por JS o usar un polyfill de terceros.
Ejemplo de uso:
ctx.ellipse(20, 21, 10, 10, 0, 0, Math.PI*2, true);
Puede utilizar un canvas-5-polyfill para proporcionar el método de elipse.
O simplemente pegue un poco de código js para proporcionar el método de elipse:
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(); } }
fuente
Esta es otra forma de crear una forma de elipse, aunque usa la función "fillRect ()", esta se puede usar para cambiar los argumentos en la función 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>
fuente
Con esto incluso puedes dibujar segmentos de una elipse:
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() } }
http://jsfiddle.net/FazAe/1/
fuente
Como a nadie se le ocurrió un enfoque que utilice el más simple
quadraticCurveTo
, estoy agregando una solución para eso. Simplemente reemplace lasbezierCurveTo
llamadas en la respuesta de @ Steve con esto:También puede eliminar el
closePath
. Sin embargo, el óvalo se ve ligeramente diferente.fuente
Aquí hay una función que escribí que usa los mismos valores que el arco de elipse en SVG. X1 e Y1 son las últimas coordenadas, X2 e Y2 son las coordenadas finales, el radio es un valor numérico y el sentido de las agujas del reloj es un valor booleano. También asume que su contexto de lienzo ya ha sido definido.
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); }
fuente
Si desea que la elipse quepa completamente dentro de un rectángulo, es realmente así:
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; }
Asegúrese
canvasContext.fillStyle = 'rgba(0,0,0,0)';
de no llenar con este diseño.fuente