In canvas, you can easily draw a circle using the arc method. Originally, the circle can also be regarded as an ellipse with equal width and height, but there is no way to draw an ellipse in canvas. We need to use other methods to simulate it.
First of all, we need to clarify the parameters required to draw an ellipse. Basic geometric knowledge tells us that the ellipse needs the center coordinates, width, height, or rotation angle, but this can be avoided for the time being, rotation is relatively easy.
1. Use lineTo to draw an ellipse You read that right, lineTo, a method that is purely used to draw straight lines, can actually be used to draw ellipses! ? But he does exist, but the writing style is really incredible:function DrawEllipse(Canvas,O,OA,OB){
//Draw an ellipse, example: var B=new Array(150,150); DrawEllipse(hb,B,50,30);
with (Canvas){
var x=O[0]+OA;
var y=O[1];
moveTo(x,y);
for (var i=0;i<=360;i++){
var ii=i*Math.PI/180;
var x=O[0]+OA*Math.cos(ii);
var y=O[1]-OB*Math.sin(ii);
lineTo(x,y);
}
}
}
The principle of this method is that a circle has 360 degrees, so use lineTo to loop 360 times, draw the line segments of each degree, and finally connect them into an ellipse. Among them, the trigonometric function sine cosine needs to be used for calculation.
Note that the second parameter of this method is an array, that is, the center coordinates of the ellipse.
The idea is very strange, and the ellipse drawn is relatively smooth. But it is not worth using - this method requires 360 cycles for every ellipse drawn. Only when the ellipse is drawn is a slightest test of the performance of the browser.
We just need to understand his ideas
2. Use arc to draw a circle and scale it into an ellipse The original text of this method is here, and the core function is as follows:var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = 0;
var centerY = 0;
var radius = 50;
// save state
context.save();
// translate context
context.translate(canvas.width / 2, canvas.height / 2);
// scale context horizontally
context.scale(2, 1);
// draw circle which will be stretched into an oval
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// restore to original state
context.restore()
This method uses a canvas function that I have not mentioned before, that is, scale, which can realize the scaling of canvas. Scaling has two directions: horizontal and vertical. The code enlarges the horizontal direction of canvas, while the vertical direction remains unchanged. So, the circle drawn by arc turns into an ellipse.
This method is very wonderful at first glance, with few codes, and the principles are simple and easy to understand. But after analyzing it, you will find his obvious shortcomings, that is, it is - inaccurate! For example, I need an ellipse that is 171 wide and 56 high. If we set the radius of arc to 28, then the subsequent number of 171/28/2 will be depressed.
However, there is a compromise method to always set the radius of arc to 100, and then, if it is not enough, it will be enlarged, and if it exceeds it, it will be reduced. However, it is still inaccurate.
3. Use Bezier CurveTo Since I felt that the scaling method above is inaccurate, I really want to find an exact method to draw ellipses, and finally found it on stackoverflow:function drawEllipse(ctx, x, y, w, h) {
var kappa = 0.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();
ctx.stroke();
}
This method can be considered perfect. He divided an ellipse into 4 Bezier curves and connected them to an ellipse. The final width and height are also relatively accurate and the overhead is less.
But this method still has its drawbacks. Everyone looks at the kappa parameter, and there is a very strange value. I believe that many people don’t understand why they have to take this value before the geometry expert tells you why they want to take this value - I still don’t know it. And I had a strong urge to change him and see what the consequences were.
Of course, my impulse similar to obsessive-compulsive disorder patients cannot be said to be the disadvantage of this method. His real disadvantage is - why do you need to use 4 Bezier curves? I personally think that an ellipse is obviously composed of two Bezier curves rather than four. This idea eventually led me to find the most perfect way to draw an ellipse.
4. Use two Besel curves to draw an ellipse
In order to understand whether the previous method can be streamlined, I specially registered a stackoverflow account to ask questions. Since there are pictures in the question, the points cannot be transmitted. I had to use my barely English proficiency to answer foreigners’ questions to earn points. But in the end, good luck came, and answering a question solved my points problem.
I have raised the problem of the relationship between Besel curve and ellipse.
To be honest, I didn’t understand most of the foreigners’ answers below, but fortunately he provided a code sample page, which made me understand the principle, and I would like to thank him again. According to his answer, the method I found for drawing ellipses is as follows:
//oval
CanvasRenderingContext2D.prototype.oval = function (x, y, width, height) {
var k = (width/0.75)/2,
w = width/2,
h = height/2;
this.beginPath();
this.moveTo(x, yh);
this.bezierCurveTo(x+k, yh, x+k, y+h, x, y+h);
this.bezierCurveTo(xk, y+h, xk, yh, x, yh);
this.closePath();
return this;
}
This method is both accurate and has little code, and there is no strange and difficult to understand. Just remember this, the coordinate ratio of the width of the ellipse to the control point of the Besel curve that draws the ellipse is as follows:
The Bessel control point x=(ellipse width/0.75)/2 is already reflected in the code.
You can test the above four methods to draw an ellipse.
If you find an easier way, please share it and discuss it.