Создатели API для отрисовки различных графических примитивов не предусмотрели наличие встроенной функции для подобных целей. К счастью, ее реализация не очень сложна и, столкнувшись с необходимостью рисовать эллипс на canvas, я придумал следующее решение.
function drawEllipse(ctx, x, y, a, b) { // Запоминаем положение системы координат (CК) и масштаб ctx.save(); ctx.beginPath(); // Переносим СК в центр будущего эллипса ctx.translate(x, y); /* * Масштабируем по х. * Теперь нарисованная окружность вытянется в a / b раз * и станет эллипсом */ ctx.scale(a / b, 1); // Рисуем окружность, которая благодаря масштабированию станет эллипсом ctx.arc(0, 0, b, 0, Math.PI * 2, true); // Восстанавливаем СК и масштаб ctx.restore(); ctx.closePath(); ctx.stroke();
Как видно, функция drawEllipse принимает пять аргументов.
- ctx – контекст отрисовки
- x, y – координаты центра эллипса (точки O на картинке)
- a – большая полуось («горизонтальный радиус»)
- b – малая полуось («вертикальный радиус»)
Перенос системы координат в центр эллипса необходим для того, чтобы избежать смещения его центра вправо. Наша окружность растягивается не влево и вправо одновременно а только вправо. Что произошло бы, не перенеси мы систему координат, вам пояснит картинка.
Как видно, центр окружности (точка O), координаты которого указываются при в аргументах функции, не совпадает с центром нарисованного эллипса (точка O1).
Для удобства функцию drawEllipse можно добавить в прототип объекта CanvasRenderingContext2D.
CanvasRenderingContext2D.prototype.drawEllipse = function (x, y, a, b) { // Запоминаем положение системы координат (CК) и масштаб this.save(); this.beginPath(); // Переносим СК в центр будущего эллипса this.translate(x, y); /* * Масштабируем по х. * Теперь нарисованная окружность вытянется в a / b раз * и станет эллипсом */ this.scale(a / b, 1); // Рисуем окружность, которая благодаря масштабированию станет эллипсом this.arc(0, 0, b, 0, Math.PI * 2, true); // Восстанавливаем СК и масштаб this.restore(); this.closePath(); }
Тогда её можно вызывать следующим образом:
ctx.drawEllipse(x, y, a, b);
Здесь ctx — контекст отрисовки.
В этой реализации я убрал из тела функции метод stroke(), поэтому чтобы эллипс появился на холсте необходимо ещё дописать
ctx.stroke();
Если есть желание, то можно с лёгкостью реализовать отрисовку закрашенных эллипсов просто заеменив cxt.stroke() на ctx.fill(), можно реализовать отрисовку повёрнутых на заданный угол эллипсов при помощи метода rotate контекста отрисовки. Я поленился и не стал добавлять в демо всех описанных мной дополнительных возможностей.