Рисование кривых в виде стрелки в HTML5 Canvas - PullRequest
5 голосов
/ 16 января 2012

Я пытаюсь нарисовать 3 стрелки ниже. Я могу нарисовать верхнюю правильно, но я не могу правильно нарисовать две другие стрелки. Я использую HTML5 Canvas для рисования этих стрелок.

enter image description here

Проблема возникает с моими вызовами arcTo. Я просто не могу получить правильную кривую по какой-то причине. Может быть, я должен использовать кривую Безье? Может ли кто-нибудь сказать мне, какие функции HTML5 / Javascript я использую для создания указанных выше стрелок?

Можете ли вы привести пример того, как достичь указанных стрелок?

Вот JSFiddle, чтобы показать, что идет не так: http://jsfiddle.net/hJX8X/

<canvas id="testCanvas" width="400px" height="400px">

</canvas>
<script type="text/javascript">
<!--
    var canvas = document.getElementById("testCanvas");
    var dc     = canvas.getContext("2d");

    // Points which are correct (when I draw straight lines its a perfect arrow
    var width    = 400;
    var height   = 100;
    var arrowW   = 0.35 * width;
    var arrowH   = 0.75 * height;
    var p1       = {x: 0,              y: (height-arrowH)/2};
    var p2       = {x: (width-arrowW), y: (height-arrowH)/2};
    var p3       = {x: (width-arrowW), y: 0};
    var p4       = {x: width,          y: height/2};
    var p5       = {x: (width-arrowW), y: height};
    var p6       = {x: (width-arrowW), y: height-((height-arrowH)/2)};
    var p7       = {x: 0,              y: height-((height-arrowH)/2)};

    dc.clearRect(0, 0, canvas.width, canvas.height);
    dc.fillStyle = "#FF0000";

    dc.beginPath();

    dc.moveTo(p1.x, p1.y);
    dc.lineTo(p2.x, p2.y);
    dc.lineTo(p3.x, p3.y);      
    dc.moveTo(p3.x, p3.y);
    dc.arcTo(p3.x, p3.y, p4.x, p4.y, 50);
    dc.moveTo(p4.x, p4.y);
    dc.arcTo(p4.x, p4.y, p5.x, p5.y, 50);
    dc.moveTo(p5.x, p5.y);
    dc.lineTo(p6.x, p6.y);
    dc.lineTo(p7.x, p7.y);

    dc.closePath();
    dc.fill();

    /* Draw arrow without curves
    dc.moveTo(p1.x, p1.y);
    dc.lineTo(p2.x, p2.y);
    dc.lineTo(p3.x, p3.y);      
    dc.lineTo(p4.x, p4.y);  
    dc.lineTo(p5.x, p5.y);
    dc.lineTo(p6.x, p6.y);
    dc.lineTo(p7.x, p7.y);
    */
-->
</script>

1 Ответ

9 голосов
/ 16 января 2012

Итак, у нас есть этот путь, который делает стрелку. Я прокомментировал это:

dc.moveTo(p1.x, p1.y);
dc.lineTo(p2.x, p2.y); // end of main block
dc.lineTo(p3.x, p3.y); // topmost point     
dc.lineTo(p4.x, p4.y); // endpoint 
dc.lineTo(p5.x, p5.y); // bottommost point 
dc.lineTo(p6.x, p6.y); // end at bottom point 
dc.lineTo(p7.x, p7.y);

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

Итак, мы будем использовать квадратичные кривые, которые похожи на кривые Безье, но имеют только одну контрольную точку, что делает их довольно простыми. Они работают путем указания контрольной точки, например this (слева) .

Итак, мы берем один и тот же точный код стрелки и вставляем два квадратичных Безье, чтобы сделать тощую стрелку. Мы хотим, чтобы контрольные точки были как бы "внутри" массы стрелки, чтобы квадратики изгибались внутрь:

dc.moveTo(p1.x, p1.y);
dc.lineTo(p2.x, p2.y); // end of main block
dc.lineTo(p3.x, p3.y); // topmost point
// control point is based on p3 (topmost point)
dc.quadraticCurveTo(p3.x + 20, p3.y + 30, p4.x, p4.y); // endpoint 
// control point is based on p5 (bottommost point)
dc.quadraticCurveTo(p5.x + 20, p5.y - 30, p5.x, p5.y); // bottommost point 
dc.lineTo(p6.x, p6.y); // end at bottom point 
dc.lineTo(p7.x, p7.y);

Или толстая, мы помещаем контрольную точку на той же высоте, что и самая верхняя и самая нижняя точки, и вокруг того же X, что и конечная точка:

dc.beginPath();
// Draw arrow without curves
dc.moveTo(p1.x, p1.y);
dc.lineTo(p2.x, p2.y); // end of main block
dc.lineTo(p3.x, p3.y); // topmost point
// control point is based on p3 (topmost point)
dc.quadraticCurveTo(p3.x + 120, p3.y, p4.x, p4.y); // endpoint 
// control point is based on p5 (bottommost point)
dc.quadraticCurveTo(p5.x + 120, p5.y, p5.x, p5.y); // bottommost point 
dc.lineTo(p6.x, p6.y); // end at bottom point 
dc.lineTo(p7.x, p7.y);

Живой пример здесь: http://jsfiddle.net/Yp7DM/

...