Как нарисовать овал на холсте html5?



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



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



моя цель-нарисовать некоторые глаза и на самом деле я просто использую дуги.
Спасибо заранее.



решение



so 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();
1132   16  

16 ответов:

обновления:

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

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

  • Безье-рисовать на основе верхнего левого, содержащего прямой и ширина/высота
  • Безье с Центром-рисовать на основе центра и ширины / высоты
  • дуги и масштабирование-рисование на основе круга рисования и масштабирования
  • квадратичные кривые-ничья с квадратиками
    • тест, кажется, не рисует совсем то же самое, может быть реализация
    • посмотреть oyophant это ответ
  • эллипс холста-использование W3C стандартный эллипс () метод
    • тест, кажется, не рисует совсем то же самое, может быть реализация
    • посмотреть Локтар это ответ

Оригинал:

Если вы хотите симметричный овал, вы всегда можете создать круг шириной радиуса, а затем масштабировать его до нужной высоты (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>

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

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

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

Добавление параметра rotationAngle позволяет поворачивать овал вокруг его центра на любой угол. Частичные овалы можно нарисовать, изменив диапазон (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

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

www.tinaja.com/glib/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();


}

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

Я сделал небольшую адаптацию код (частично представлено Андреем Старосчиком) для людей, которые не хотят такого общего эллипса и у которых есть только большая полуось и данные эксцентриситета эллипса (хорошо для астрономических игрушек 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();
}

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

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

Демо

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

только, кажется, работает в Chrome в настоящее время

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

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.

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

пример:

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

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

Да, это возможно с двумя кривыми Безье - вот краткий туториал/пример: http://www.williammalone.com/briefs/how-to-draw-ellipse-html5-canvas/

поддержка Chrome и Opera эллипс метод для холста 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);

можно использовать холст-5-полифилл обеспечить метод эллипса.

или просто вставить 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

Это еще один способ создания эллипса, как форма, хотя он использует функцию "fillRect()" это может быть использовано для изменения аргументов в функции 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>

С помощью этого вы можете даже нарисовать сегменты эллипса:

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/

так как никто не придумал подход, используя более простой quadraticCurveTo Я добавляю решение для этого. Просто замените bezierCurveTo звонит в @Steve's ответ С этого:

  ctx.quadraticCurveTo(x,y,xm,y);
  ctx.quadraticCurveTo(xe,y,xe,ym);
  ctx.quadraticCurveTo(xe,ye,xm,ye);
  ctx.quadraticCurveTo(x,ye,x,ym);

вы также можете удалить closePath. Овал выглядит немного по-другому, хотя.

вот функция, которую я написал, которая использует те же значения, что и дуга эллипса в SVG. X1 & Y1-последние координаты, X2 & Y2-конечные координаты, радиус-числовое значение, а по часовой стрелке-логическое значение. Он также предполагает, что ваш контекст холста уже определен.

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

Если вы хотите, чтобы эллипс полностью вписывался в прямоугольник, это действительно так:

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

убедитесь, что ваш canvasContext.fillStyle = 'rgba(0,0,0,0)'; для без заполнения с этим дизайном.

Comments

    Ничего не найдено.