Как нарисовать овал в холсте html5? - PullRequest
73 голосов
/ 31 января 2010

Кажется, не существует встроенной функции для рисования овальной формы. Также я не ищу форму яйца.

Можно ли нарисовать овал с двумя кривыми Безье? Кто-нибудь испытал это?

Моя цель - нарисовать глаза, и я просто использую дуги. Заранее спасибо.

Решение

Таким образом, scale () изменяет масштабирование для всех следующих фигур. Save () сохраняет настройки до, а восстановление используется для восстановления настроек для рисования новых фигур без масштабирования.

Благодаря Яни

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

Ответы [ 16 ]

105 голосов
/ 31 января 2010

обновления:

  • метод масштабирования может повлиять на внешний вид ширины штриха
  • правильный метод масштабирования может сохранить ширину хода без изменений
  • canvas имеет метод эллипса, который теперь поддерживается в Chrome
  • добавлены обновленные тесты в JSBin

Пример тестирования JSBin (обновлен для проверки ответов других пользователей для сравнения)

  • Безье - рисование на основе левого верхнего угла, содержащее прямоугольник и ширину / высоту
  • Безье с центром - рисование на основе центра и ширины / высоты
  • Дуги и масштабирование - рисовать на основе круга и масштабирования.
  • Квадратичные кривые - рисовать квадратичными
  • Canvas Ellipse - с использованием стандартного метода W3C ellipse ()

Оригинал:

Если вам нужен симметричный овал, вы всегда можете создать круг ширины радиуса, а затем масштабировать его до необходимой высоты ( edit: обратите внимание, что это повлияет на появление ширины штриха - см. Ответ acdameli), но если вы хотите получить полный контроль над эллипсом, используйте один из кривых Безье.

<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>
44 голосов
/ 04 декабря 2011

Вот упрощенная версия решения в другом месте. Я рисую канонический круг, перевожу и масштабирую, а затем обводку.

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();
}
16 голосов
/ 09 марта 2012

Метод кривой Безье отлично подходит для простых овалов. Для большего контроля вы можете использовать цикл для рисования эллипса с различными значениями радиуса x и y (радиус, радиус?).

Добавление параметра axisA поворота позволяет вращать овал вокруг его центра на любой угол. Частичные овалы можно нарисовать, изменив диапазон (var i), в котором работает цикл.

Визуализация овала таким способом позволяет вам определить точное положение x, y всех точек на линии. Это полезно, если расположение других объектов зависит от расположения и ориентации овала.

Вот пример кода:

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

Смотрите интерактивный пример здесь: http://www.scienceprimer.com/draw-oval-html5-canvas

11 голосов
/ 07 октября 2012

Вам нужно 4 кривых Безье (и магическое число), чтобы надежно воспроизвести эллипс. Смотрите здесь:

www.tinaja.com / бойким / ellipse4.pdf

Два безье точно не воспроизводят эллипс. Чтобы доказать это, попробуйте некоторые из 2 решений Безье, приведенных выше, с равными высотой и шириной - они должны идеально приближаться к кругу, но не будут. Они будут выглядеть овальными, что доказывает, что они не делают то, что должны.

Вот что должно работать:

http://jsfiddle.net/BsPsj/

Вот код:

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


}
11 голосов
/ 31 января 2010

Вы также можете попробовать использовать неравномерное масштабирование. Вы можете задать масштабирование по X и Y, поэтому просто установите масштабирование по X или Y больше, чем другое, и нарисуйте круг, и вы получите эллипс.

9 голосов
/ 20 апреля 2014

Для холста теперь есть собственная функция эллипс , очень похожая на функцию дуги, хотя теперь у нас есть два значения радиуса и потрясающее вращение.

эллипс (x, y, radiusX, radiusY, вращение, startAngle, endAngle, против часовой стрелки)

Демонстрация в реальном времени

ctx.ellipse(100, 100, 10, 15, 0, 0, Math.PI*2);
ctx.fill();

В данный момент, похоже, работает только в Chrome

8 голосов
/ 07 июля 2012

Я сделал небольшую адаптацию этого кода (частично представлен Эндрю Старосчиком) для людей, которые не хотят такого общего эллипса и у которого есть только большая полуось и данные эксцентричности эллипса (например, для астрономических игрушек на JavaScript для построения орбит).

Вот, пожалуйста, помните, что в i можно адаптировать шаги для большей точности на чертеже:

/* 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();
}
4 голосов
/ 16 декабря 2011

Мое решение немного отличается от всех этих. Хотя самый близкий, я думаю, ответ, получивший наибольшее количество голосов, приведенный выше, но я думаю, что этот способ немного чище и его легче понять.

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

А затем используйте это так:

bezierCurve(x + 60, y + 75, 80, 130);

Есть несколько примеров использования в скрипке, а также неудачная попытка сделать один с использованием quadraticCurveTo.

3 голосов
/ 23 марта 2011

Мне нравится решение кривых Безье выше. Я заметил, что масштаб также влияет на ширину линии, поэтому, если вы пытаетесь нарисовать эллипс, который шире, чем он высокий, ваши верхняя и нижняя "боковые стороны" будут казаться более тонкими, чем ваши левая и правая "боковые стороны" ...

хорошим примером будет:

ctx.lineWidth = 4;
ctx.scale(1, 0.5);
ctx.beginPath();
ctx.arc(20, 20, 10, 0, Math.PI * 2, false);
ctx.stroke();

Вы должны заметить, что ширина линии на вершине и в долине эллипса в два раза меньше ширины на левой и правой вершинах (вершинах?).

2 голосов
/ 01 июня 2016

Chrome и Opera поддерживают метод эллипса для контекста Canvas 2d, но IE, Edge, Firefox и Safari не поддерживают его.

Мы можем реализовать метод эллипса с помощью JS или использовать сторонний полифилл.

ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)

Пример использования:

ctx.ellipse(20, 21, 10, 10, 0, 0, Math.PI*2, true);

Вы можете использовать canvas-5-polyfill для предоставления метода эллипса.

Или просто вставьте некоторый код js, чтобы обеспечить метод эллипса:

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

ellipse 1

ellipse 2

ellipse 3

ellipse 4

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...