Линии рисования с постоянно меняющейся шириной линии на холсте HTML

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

Эта скрипта показывает мою проблему:

http://jsfiddle.net/ZvuQG/1/

Когда вы вызываете stroke(), текущая установленная линейная ширина используется для обводки всей строки. Моя первая мысль заключалась в том, чтобы рисовать каждую фигуру отдельно, но, конечно, это оставляет заметные промежутки в линии по углам.

Какой у меня лучший вариант? Должен ли я прибегать к рисованию полигонов (трапеций), чтобы получить углы?

Есть ли более простой способ?

(Edit: Заметьте, что я не пытаюсь на самом деле рисовать эллипсы или любые другие основные фигуры, я пытаюсь построить математические функции, используя толщину линии для представления скорости)

Ответ 1

Для тех, кого это интересует, я придумал два решения своей проблемы.

Первая идея состояла в том, чтобы нарисовать каждую точку как угол, используя холст, чтобы нарисовать аккуратный угол. Демо можно увидеть по адресу:

http://jsfiddle.net/7BkyK/2/

var ctx = document.getElementById('canvas1').getContext('2d');
var points = [null, null, null];

for(var i=0; i<24; i++)
{
    var width = 0.5 + i/2;

    var m = 200;

    var x = Math.cos(i/4) * 180;
    var y = Math.sin(i/4) * 140;

    points[0] = points[1];
    points[1] = points[2];
    points[2] = { X:x, Y:y};

    if(points[0] == null)
        continue;

    var px0 = (points[0].X + points[1].X) / 2;
    var py0 = (points[0].Y + points[1].Y) / 2;

    var px1 = (points[1].X + points[2].X) / 2;
    var py1 = (points[1].Y + points[2].Y) / 2;

    ctx.beginPath();
    ctx.lineWidth = width;
    ctx.strokeStyle = "rgba(0,0,0,0.5)";
    ctx.moveTo(m+px0,m+py0);
    ctx.lineTo(m+points[1].X,m+points[1].Y);
    ctx.lineTo(m+px1,m+py1);
    ctx.stroke();
}
​

Второе и более красивое решение, предложенное Шмиддти, заключается в использовании кривых Безье. Это оказалось отличным решением:

http://jsfiddle.net/Ssrv9/1/

// 1.
// Varying line width, stroking each piece of line separately
var ctx = document.getElementById('canvas1').getContext('2d');
var points = [null, null, null, null];

for(var i=-1; i<25; i = i +1)
{
    var width = 0.5 + i/2;

    var m = 200;


    var x = Math.cos(i/4) * 180;
    var y = Math.sin(i/4) * 140;

    points[0] = points[1];
    points[1] = points[2];
    points[2] = { X:x, Y:y};

    if(points[0] == null)
        continue;


    var p0 = points[0];
    var p1 = points[1];
    var p2 = points[2];

    var x0 = (p0.X + p1.X) / 2;
    var y0 = (p0.Y + p1.Y) / 2;

    var x1 = (p1.X + p2.X) / 2;
    var y1 = (p1.Y + p2.Y) / 2;

    ctx.beginPath();
    ctx.lineWidth = width;
    ctx.strokeStyle = "black";

    ctx.moveTo(m+x0, m+y0);
    ctx.quadraticCurveTo(m+p1.X, m+p1.Y, m+x1, m+y1);
    ctx.stroke();
}

Ответ 2

Добавление закругленных штрихов и квадратичной кривой делает все это намного более аккуратным.

См. здесь, например.

Ответ 3

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

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

Это должно дать вам гладкую линию, расширяющуюся и сжимающуюся, когда она приближается к вашим точкам "круги".