Режим path mode (режим рисования пути) canvas

5 (100%) 3 vote[s]

В предыдущих примерах мы разобрали, как рисовать прямоугольники, используя методы контекста fillRect (x, y, width, height) и strokeRect (x, y, width, height), как рисовать текстовое сообщение, используя методы fillText (message, x, y)  и  strokeText (message, x, y),  которые рисуют текст в режиме заливки и каркасном режиме соответственно. Эти методы, наряду с методом drawImage (…), являются «непосредственными методами»: как только они выполняются, результаты сразу отображаются на экране, выполняются рисунки, пиксели на холсте области меняют свои цвета и т. д. Режим пути (path mode) = заполнить буфер, затем выполнить все буферизованные команды одновременно, чтобы включить оптимизацию и параллелизм. Режим path mode позволяет сократить время выполнения большого объема команд.

Режим immediate mode и режим path mode

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

Режим рисования контуров допускает параллелизм: если вам нужно нарисовать 10 000 прямоугольников, лучше сохранить запрос на видеокарте, а затем выполнить рисование сразу, а не, например, делать 10 000 непосредственных вызовов функции strokeRect (…). В режиме буферизации графический процессор (GPU) аппаратного обеспечения графической карты сможет распараллеливать вычисления (современные графические карты могут выполнять сотни/тысячи задач параллельно).

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

Your browser does not support the canvas tag.

Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5. <script>
  6. var canvas, ctx, w, h;
  7.  
  8. function init() {
  9.   canvas = document.getElementById('myCanvas');
  10.   ctx = canvas.getContext('2d');
  11.  
  12.   w = canvas.width;
  13.   h = canvas.height;
  14.  
  15.   console.time("time to draw");
  16.  
  17.   for(var i=0; i < 1000; i++) {
  18.     var x = Math.random() * w;
  19.     var y = Math.random() * h;
  20.     var width = Math.random() * w;
  21.     var height = Math.random() * h;
  22.  
  23.     ctx.strokeRect(x, y, width, height); 
  24.   }
  25.   console.timeEnd("time to draw");
  26. }
  27.  
  28. </script>
  29.   </head>
  30. <body onload = init();>
  31. <canvas id="myCanvas" width="400" height =400>
  32.   Your browser does not support the canvas tag.</canvas>
  33. </body>
  34. </html>

Измеряем время, используя обычные  console.time (name_of_timer) и console.timeEnd (name_of_timer), которые будут записывать в консоли браузера прошедшее время.

А теперь тот же рисунок с используя режим path mode:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5. <script>	  
  6. var canvas, ctx, w, h;
  7.  
  8. function init() {
  9.   canvas = document.getElementById('myCanvas1');
  10.   ctx = canvas.getContext('2d');
  11.  
  12.   w = canvas.width;
  13.   h = canvas.height;
  14.  
  15.   console.time("time to draw");
  16.  
  17.   for(var i=0; i < 1000; i++) {
  18.     var x = Math.random() * w;
  19.     var y = Math.random() * h;
  20.     var width = Math.random() * w;
  21.     var height = Math.random() * h;
  22.  
  23.     ctx.rect(x, y, width, height); 
  24.   }
  25.   ctx.stroke();
  26.   console.timeEnd("time to draw");
  27. }
  28. </script>
  29.   </head>
  30. <body onload = init();>
  31. <canvas id="myCanvas1" width="400" height =400>
  32.   Your browser does not support the canvas tag.</canvas>
  33. </body>
  34. </html>

Что мы получим:

    • Вместо вызова strokeRect (…) или fillRect (…), мы просто вызываем метод rect (…) контекста (строка 21). Вот как мы можем отложить рисование прямоугольников. 1000 прямоугольников хранятся в буфере в оборудовании.
    • Вызов ctx.stroke ()  (строка 25) или его родственного метода ctx.fill () отрисовывает все содержимое буфера в режиме заполнения или обводки.
    • И вот что дает таймер: немного более быстрое время выполнения. Изменение 1000 на 100 000 даст еще большие различия.

Сброс буфера режима пути:

Вызов ctx.beginPath () сбросит буфер (очистит его содержимое). 

Режим path mode, краткое изложение принципов:

    1. Вызовите методы рисования, которые работают в режиме пути , например, вызовите ctx.rect (…) вместо ctx.strokeRect (…) или ctx.fillRect (…)
    2. Вызовите ctx.stroke () или ctx.fill (), чтобы нарисовать содержимое буфера,
    3. Помните, что буфер никогда не самоочищается: два последовательных вызова ctx.stroke () будут извлекать содержимое буфера дважды! Вместо этого используйте ctx.beginPath (), чтобы очистить его при необходимости.
    4. Можно очистить буфер, вызвав ctx.beginPath ().
    5. Режим path mode быстрее, чем immediate mode (возможно распараллеливание).

Режим path mode: рисование линий

Давайте пойдем немного дальше, введя понятие «path drawing» ( рисование контура). Этот подход использует метод контекста ctx.moveTo (x, y) в сочетании с другими методами рисования, которые заканчиваются на «To», такими как  ctx.lineTo (x, y) .

Это облегчает рисование нескольких связанных линий. Последовательные вызовы ctx.lineTo (x, y) сохранят в пути/буфере набор связанных линий, которые мы будем рисовать в целом одним вызовом ctx.stroke () или ctx.fill () .

Вот основные шаги:

    1. Положите виртуальный «перо» куда-нибудь с помощью вызова ctx.moveTo (x1, y1) . Это будет исходная точка.
    2. Вызовите метод ctx.lineTo (x2, y2), чтобы нарисовать линию от предыдущей позиции (предыдущего шага) до позиции, переданной в качестве параметров в метод lineTo (…) . Эта позиция будет служить источником для следующей линии, которая будет нарисована.
    3. Вызов lineTo (x3, y3) еще раз , чтобы нарисовать линию, которая идет от (x2, y2) до (x3, y3). Эта строка начнется в конце предыдущей.
    4. Повторите шаг 3, чтобы нарисовать больше связанных линий.
    5. Вызовите методы ctx.stroke () или ctx.fill (), чтобы нарисовать путь, определенный различными линиями.

Обратите внимание, что вызов ctx.stroke () или ctx.fill () будет использовать текущие значения свойств strokeStyle или fillStyle. Можно вызвать ctx.moveTo (x, y) между шагов 1-5, чтобы переместить «перо» в другое место, не подключаясь к последней нарисованной линии.

Нарисуем таким способом сетку:

Your browser does not support the canvas tag.

В этом примере вся сетка рисуется во время выполнения последней строки кода, с единственным вызовом ctx.stroke (). Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.  
  6.   </head>
  7. <body>
  8.  
  9. <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
  10.  
  11. <script type="text/javascript">
  12. var canvas=document.getElementById('myCanvas');
  13. var ctx=canvas.getContext('2d');
  14.   // Вертикальные линии
  15. for (var x = 0.5; x < 500; x += 10) { 
  16.   ctx.moveTo(x, 0);  
  17.   ctx.lineTo(x, 375);
  18. }
  19. // Горизонтальные линии  
  20. for (var y = 0.5; y < 375; y += 10) {
  21.   ctx.moveTo(0, y);  
  22.   ctx.lineTo(500, y);
  23. }
  24. // Рисуем синим
  25.     ctx.strokeStyle = "#0000FF";
  26. // До выполнения следующей строки ничего не было нарисовано!
  27.   ctx.stroke(); 
  28.  
  29. </script>
  30.  
  31. </body>
  32. </html>

Другой пример смешивания заполненных и каркасных форм (а также непосредственного режима и режима пути):

Your browser does not support the canvas tag.

Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6.  
  7. <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
  8.  
  9. <script type="text/javascript">
  10. var canvas=document.getElementById('myCanvas');
  11. var ctx=canvas.getContext('2d');
  12.  
  13. ctx.fillStyle='#FF0000';
  14. ctx.fillRect(0,0,80,100);
  15.  
  16. ctx.moveTo(0,0);
  17. ctx.lineTo(100, 100); 
  18. ctx.lineTo(100,0);
  19.  
  20. ctx.strokeStyle = "#0000FF";
  21. ctx.stroke();
  22.  
  23. </script>
  24.  
  25. </body>
  26. </html>

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

А вот пример рисование одиночной траектории с отключенными линиями/частями:

Your browser does not support the canvas tag.

Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6.  
  7. <canvas id="myCanvas4">Your browser does not support the canvas tag.</canvas>
  8.  
  9. <script type="text/javascript">
  10. var canvas=document.getElementById('myCanvas4');
  11. var ctx=canvas.getContext('2d');
  12. // первая часть пути
  13. ctx.moveTo(20,20);
  14. ctx.lineTo(100, 100);
  15. ctx.lineTo(100,0);
  16. // вторая часть пути, moveTo (...) используется для «прыжка» в другое место
  17. ctx.moveTo(120,20);
  18. ctx.lineTo(200, 100);
  19. ctx.lineTo(200,0);
  20. // указать цвет обводки + нарисовать путь
  21. ctx.strokeStyle = "#0000FF";
  22. ctx.stroke();
  23.  
  24. </script>
  25.  
  26. </body>
  27. </html>

В этом примере мы просто вызывали метод moveTo () между каждой частью пути. И мы вызвали stroke () только один раз, чтобы нарисовать весь путь.

Рисование линий в разных стилях

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

Распространенная ошибка: рисовать один и тот же путь дважды

 Неправильный путь:

Попробуем вызывать stroke () после первой половины пути, а затем вызывать fill () после второй половины пути:

  1. var canvas=document.getElementById('myCanvas');
  2. var ctx=canvas.getContext('2d');
  3. // первая часть пути
  4. ctx.moveTo(20,20);
  5. ctx.lineTo(100, 100);
  6. ctx.lineTo(100,0);
  7. // указать цвет обводки + нарисовать первую часть пути
  8. ctx.strokeStyle = "#0000FF";
  9. ctx.stroke();
  10. // вторая часть пути
  11. ctx.moveTo(120,20);
  12. ctx.lineTo(200, 100);
  13. ctx.lineTo(200,0);
  14. // указать цвет обводки + нарисовать путь
  15. ctx.fillStyle = "pink";
  16. ctx.fill();

Это не работает! Как ни странно, две части пути залиты розовым цветом:

По логике мы поступили правильно: мы вызвали  stroke()  после того, как была нарисована первая половина пути (линии 5-8). Тогда мы вызывали fill () только после того, как была указана вторая часть пути (строки 14-19) … так что случилось?

Помните, что fill () или stroke () рисует весь путь, даже если он отключен и даже если он уже нарисован!

Что произошло:

    1. Вызов stroke () нарисовал путь, соответствующий линиям 5-7. Действительно, первая часть пути (слева) фактически была нарисована один раз в каркасном режиме и синим цветом.
    2. Затем вызов функции fill ()  в строке 20 снова нарисовал весь путь, но в розовом и в заполненном режиме. Но на этот раз путь соответствует линиям 5-7 плюс линии 14-16, которые составляют вторую фигуру справа. Итак, путь, который был проложен на этот раз, состоит уже из обоих треугольников.

Правильный путь:

Если вы не хотите рисовать части одного и того же пути несколько раз, вам нужно нарисовать два разных пути, используя метод ctx.beginPath () , как показано в следующем примере:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.       <title>Canvas</title>
  6.   </head>
  7. <body>
  8.  
  9. <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
  10.  
  11. <script type="text/javascript">
  12. var canvas=document.getElementById('myCanvas');
  13. var ctx=canvas.getContext('2d');
  14.  
  15. // первая часть пути
  16. ctx.moveTo(20,20);
  17. ctx.lineTo(100, 100); 
  18. ctx.lineTo(100,0);
  19.  
  20. // указать цвет обводки + нарисовать первую часть пути
  21. ctx.strokeStyle = "#0000FF";
  22. ctx.stroke();
  23.  
  24. // начать новый путь, очистить текущий буфер
  25. ctx.beginPath();
  26.  
  27. // вторая часть пути
  28. ctx.moveTo(120,20);
  29. ctx.lineTo(200, 100); 
  30. ctx.lineTo(200,0);
  31.  
  32. // указать цвет обводки + нарисовать путь
  33. ctx.fillStyle = "pink";
  34. ctx.fill();
  35.  
  36. </script>
  37.  
  38. </body>
  39. </html>

Теперь получим ожидаемый рисунок:

На этот раз, чтобы нарисовать две фигуры по-разному, мы определили два отдельных пути. Способ сделать это — просто вызвать ctx.beginPath (), чтобы начать новый путь. В этом примере первый путь был нарисован в режиме каркаса, затем был запущен новый путь, который нарисован в режиме заливки.

Рисование линий в immediate mode, рисование стрелок

Иногда может быть полезно нарисовать только одну линию, не находясь на одном пути. Рассмотрим функцию «нарисовать линию», которая принимает начальную и конечную координаты, цвет, ширину линии и т. д.:

Вот исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11. var canvas=document.getElementById('myCanvas');
  12. var ctx=canvas.getContext('2d');
  13.  
  14.   drawLine(0, 0, 100, 100);
  15.   drawLine(0, 50, 150, 200, 'red');
  16.   drawLine(10, 100, 100, 10, 'green', 10);
  17.  
  18. function drawLine(x1, y1, x2, y2, color, width) {
  19.   ctx.save();
  20.  
  21.   // установить цвет и lineWidth, если эти параметры
  22.    // не определены, ничего не делать (значения по умолчанию)
  23.   if(color)
  24.       ctx.strokeStyle = color;
  25.  
  26.   if(width)
  27.       ctx.lineWidth = width;
  28.  
  29.   // начать новый путь
  30.   ctx.beginPath();
  31.  
  32.   ctx.moveTo(x1, y1);
  33.   ctx.lineTo(x2, y2);
  34.   ctx.stroke();
  35.   ctx.restore();
  36. }
  37.  
  38. </script>
  39.  
  40. </body>
  41. </html>

Обратите внимание на сохранение/восстановление контекста в начале/конце функции. Это хорошая практика, чтобы избежать влияния на контекст других функций.

Рисование стрелок

Рассмотрим функцию, которая рисует стрелки на холсте, как на рисунке ниже:

Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11.  
  12.   var canvas=document.getElementById('myCanvas');
  13.   var ctx=canvas.getContext('2d');
  14.  
  15.   function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
  16.    // переменные, которые будут использоваться при создании стрелки
  17.     var headlen = 10;
  18.     var angle = Math.atan2(toy-fromy,tox-fromx);
  19.  
  20.     ctx.save();
  21.     ctx.strokeStyle = color;
  22.  
  23.     // начальный путь стрелки от начального квадрата до конечного квадрата
  24.      // и рисуем обводку
  25.     ctx.beginPath();
  26.     ctx.moveTo(fromx, fromy);
  27.     ctx.lineTo(tox, toy);
  28.     ctx.lineWidth = arrowWidth;
  29.     ctx.stroke();
  30.  
  31.    // начинаем новый путь от головы стрелки к одной из сторон
  32.      //точки
  33.     ctx.beginPath();
  34.     ctx.moveTo(tox, toy);
  35.     ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),
  36.                toy-headlen*Math.sin(angle-Math.PI/7));
  37.  
  38.    // путь от боковой точки стрелки к другой боковой точке
  39.     ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),
  40.                toy-headlen*Math.sin(angle+Math.PI/7));
  41.  
  42.    // путь от боковой точки до кончика стрелки, а затем
  43.      // снова в противоположную сторону
  44.     ctx.lineTo(tox, toy);
  45.     ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),
  46.                toy-headlen*Math.sin(angle-Math.PI/7));
  47.  
  48.     // рисует пути, созданные выше
  49.     ctx.stroke();
  50.     ctx.restore();
  51. }
  52. drawArrow(ctx, 10, 10, 100, 100, 10, 'red');  
  53. drawArrow(ctx, 100, 10, 140, 140, 3, 'black');
  54.   </script>
  55.  
  56. </body>
  57. </html>

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

Этот веб-сайт стоит прочитать:  http://www.dbp-consulting.com/tutorials/canvas/CanvasArrow.html. В нем подробно рассказывается, как рисовать стрелки с изогнутыми головками и различные стили для головы. Однако обратите внимание, что вам нужно будет изменить некоторые детали, если вы хотите, чтобы они поддерживали разную ширину линии и т. д.

Наглядная реализация этого примера имеется в первой статье.

Закрытие пути

Метод ctx.closePath () указывает, что нам нужен замкнутый путь: рисовать от последней точки до первой. Пример:

Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11. var canvas=document.getElementById('myCanvas');
  12. var ctx=canvas.getContext('2d');
  13.  
  14. // Путь из трех точек (определяет две линии)
  15. ctx.moveTo(20,20);
  16. ctx.lineTo(100, 100); 
  17. ctx.lineTo(100,0);
  18.  
  19. // Закрыть путь
  20. ctx.closePath();
  21.  
  22. // указать цвет обводки + нарисовать первую часть пути
  23. ctx.strokeStyle = "blue";
  24. ctx.stroke();
  25.  
  26. </script>
  27.  
  28. </body>
  29. </html>

Рисование кругов и дуг

Lля рисования дуг окружностей используется метод ctx.arc (cx, cy, radius, startAngle, endAngle, drawInverse). Он принимает центр круга/дуги, его радиус, начальный угол дуги (поворот по часовой стрелке), конечный угол дуги и еще один необязательный параметр, о котором мы поговорим позже:

Типичное использование для рисования дуги/круга/эллипса:

ctx.arc(centerX, centerY, radius, startAngle, endAngle); // рисование по часовой стрелке
ctx.arc(centerX, centerY, radius, startAngle, endAngle, false);

Углы указаны в радианах (от 0 до 2 * Math.PI) . Дуга рисуется по часовой стрелке. Помните, что это может показаться непривычным, если вы привыкли к тригонометрической записи.

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

Пример 1: рисование дуги с радиусом = 50, начальный угол = 0, конечный угол = PI / 2:

Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas" width="500">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11. var canvas=document.getElementById('myCanvas');
  12. var ctx=canvas.getContext('2d');
  13.  
  14.  
  15. ctx.beginPath();
  16. // пытаемся установить последний параметр в true или удалить его
  17. ctx.arc(100,75,50,0,Math.PI/2);
  18.  
  19. ctx.lineWidth=10;
  20. ctx.stroke();
  21.  
  22. </script>
  23.  
  24. </body>
  25. </html>

Если мы изменим последний параметр (мы его пропустили, поэтому он принял значение false по умолчанию):

ctx.beginPath();
// мы задаем последний параметр
ctx.arc(100, 75, 50, 0, Math.PI/2, true);
 
ctx.lineWidth = 10;
ctx.stroke();

и в результате получаем результат, «дополнительный» по предыдущей дуге:

Пример 2: рисование полного круга (заполнено + выделено)

Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.         <style>
  6.             body {
  7.                 margin: 0px;
  8.                 padding: 0px;
  9.             }
  10.  
  11.             #myCanvas {
  12.                 border: 1px solid #9C9898;
  13.             }
  14.         </style>
  15.         <script>
  16.             window.onload = function(){
  17.                 var canvas = document.getElementById("myCanvas");
  18.                 var ctx = canvas.getContext("2d");
  19.                 var centerX = canvas.width / 2;
  20.                 var centerY = canvas.height / 2;
  21.                 var radius = 70;
  22.  
  23.                 ctx.beginPath();
  24.  
  25.                // Добавить к пути полный круг (от 0 до 2PI)
  26.                 ctx.arc(centerX, centerY, radius, 0, 2*Math.PI, false);
  27.  
  28.                // При рисовании пути вы можете изменить контекст
  29.                  // свойства, пока не будет выполнен вызов stroke () или fill ()
  30.                 ctx.fillStyle = "lightBlue";
  31.                 // Рисует закрашенный круг голубым цветом
  32.                 ctx.fill();
  33.  
  34.                // Готовимся к наброску
  35.                 ctx.lineWidth = 5;
  36.                 ctx.strokeStyle = "black";
  37.  
  38.                // рисует снова путь (круг) 
  39.                  // в каркасе
  40.                 ctx.stroke();
  41.  
  42.               // Обратите внимание, что мы вызвали context.arc () только один раз! И нарисовал его дважды
  43.                // с разными стилями
  44.             };
  45.         </script>
  46.     </head>
  47.     <body>
  48.         <canvas id="myCanvas" width="578" height="200">
  49.         </canvas>
  50.     </body>
  51. </html>

Рисование прямоугольников со скругленными углами

Есть еще один метод ctx.arcTo (x1, y1, x2, y2, radius), который немного сложен в использовании, но очень удобен для рисования скругленных прямоугольников.

Фактически, метод arcTo (…) рисует дугу окружности в зависимости от некоторых касательных:

Типичное использование:

    1. Проведите воображаемую линию через (x0, y0) и (x1, y1) , проведите другую воображаемую линию через (x1, y1) и (x2, y2),
    2. Возьмите воображаемый круг радиуса r и сдвиньте его между двумя линиями, пока он не коснется обеих линий. Две точки, в которых окружность касается линий, называются точками касания.
    3. arcTo (x1, y1, x2, y2, r) проведет линию от текущей точки (x0, y0) до первой точки касания на линии от (x0, y0) до (x1, y1),
    4. Он также проведет дугу от этой точки касания к другой точке касания на линии от (x1, y1) до (x2, y2) вдоль окружности круга.
    5. Наконец, он добавляет точку касания, где дуга заканчивается, на линии от (x1, y1) до (x2, y2) к траектории в качестве новой текущей точки на траектории.

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

Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas" height = 400 width="800">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11. var canvas=document.getElementById('myCanvas');
  12. var context=canvas.getContext('2d');
  13.  
  14. context.beginPath();
  15. context.moveTo(0, 20);
  16. context.arcTo(100, 100, 200, 20, 50);
  17.  
  18. context.lineWidth = 5;
  19. context.strokeStyle = "#0000ff";
  20. context.stroke();
  21.  
  22. </script>
  23.  
  24. </body>
  25. </html>

Пример 2: скругленный прямоугольник

Исходный код:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11.  
  12. var roundedRect=function(ctx,x,y,width,height,radius,fill,stroke)
  13. {
  14.     ctx.beginPath();
  15.  
  16.    // рисуем верхнюю сторону и верхний правый угол
  17.     ctx.moveTo(x+radius,y);
  18.     ctx.arcTo(x+width,y,x+width,y+radius,radius);
  19.  
  20.    // рисуем правую сторону и правый нижний угол
  21.     ctx.arcTo(x+width,y+height,x+width-radius,y+height,radius); 
  22.  
  23.     // рисуем нижнюю сторону и нижний левый угол
  24.     ctx.arcTo(x,y+height,x,y+height-radius,radius);
  25.  
  26.     // рисуем левый бок и верхний левый угол
  27.     ctx.arcTo(x,y,x+radius,y,radius);
  28.  
  29.     if(fill){
  30. 	    ctx.fill();
  31.     }
  32.     if(stroke){
  33. 	    ctx.stroke();
  34.     }
  35. }  
  36.  
  37. var canvas=document.getElementById('myCanvas');
  38. var ctx=canvas.getContext('2d');
  39.  
  40. ctx.strokeStyle='rgb(150,0,0)';
  41. ctx.fillStyle='rgb(0,150,0)';
  42. ctx.lineWidth=7;
  43. roundedRect(ctx,15,15,160,120,20,true,true);
  44.  
  45. </script>
  46.  
  47. </body>
  48. </html>

Пример 3: использование незакругленные вершины в arcTo

Для рисования скругленного квадрата этот код также работает:

ctx.moveTo(x+radius, y);
ctx.arcTo(x+width, y,x+width, y+height, radius);
ctx.arcTo(x+width, y+height, x, y+height, radius); 
ctx.arcTo(x, y+height, x, y,radius);
ctx.arcTo(x, y, x+width, y,radius);

выяснить, где закончится дуга, можно следующим образом:

ctx.moveTo(x+radius, y);
ctx.arcTo(x+width, y, x+width, y+radius, radius);
ctx.arcTo(x+width, y+height, x+width-radius, y+height,radius); 
ctx.arcTo(x, y+height, x, y+height-radius, radius);
ctx.arcTo(x, y, x+radius, y,radius);

Это может быть особенно полезно, если вы имеете дело с чем-то, кроме прямоугольника, например с округленным треугольником:

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11.  
  12. var roundedTriangle=function(ctx,x1,y1,x2,y2,x3,y3,radius,fill,stroke)
  13. {
  14.     ctx.beginPath();
  15.  
  16.     // начинаем с середины стороны между x1, y1 и x2, y2
  17.     ctx.moveTo((x1+x2)/2,(y1+y2)/2);
  18.  
  19.    // обходим вершину x2, y2
  20.     ctx.arcTo(x2,y2,x3,y3,radius);
  21.  
  22.     // обходим вершину x3, y3
  23.     ctx.arcTo(x3,y3,x1,y1,radius);
  24.  
  25.   // обходим вершину x1, y1
  26.     ctx.arcTo(x1,y1,x2,y2,radius);
  27.  
  28.    // и закрываем треугольник линией к начальной точке
  29.     ctx.lineTo((x1+x2)/2,(y1+y2)/2);
  30.  
  31.     if(fill){
  32. 	    ctx.fill();
  33.     }
  34.     if(stroke){
  35. 	    ctx.stroke();
  36.     }
  37. }  
  38.  
  39. var canvas=document.getElementById('myCanvas');
  40. var ctx=canvas.getContext('2d');
  41.  
  42. ctx.strokeStyle='rgb(150,0,0)';
  43. ctx.fillStyle='rgb(0,150,0)';
  44. ctx.lineWidth=7;
  45. roundedTriangle(ctx,200,15,300,150,15,100,20,true,true);
  46.  
  47. </script>
  48.  
  49. </body>
  50. </html>

Квадратичные кривые

Квадратичные кривые определяются начальной точкой (называемой «контекстной точкой»), контрольной точкой и конечной точкой.Кривая соответствует касательным между контекстом и контрольными точками, а также между контрольной и конечной точками.

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

Контрольная точка контролирует кривизну — если мы переместим контрольную точку дальше, мы получим более острую кривую.

Типичное использование:

context.moveTo(contextX, contextY);
context.quadraticCurveTo(controlX, controlY, endX, endY);
// Необязательно: установить lineWidth и цвет обводки
context.lineWidth = 5;
context.strokeStyle = "#0000ff";
// Рисовать!
context.stroke();

Пример 1: квадратичная кривая

  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.       <meta charset="utf-8"/>
  5.   </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas1" height = 400 width="800">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11. var canvas=document.querySelector('#myCanvas1');
  12. var context=canvas.getContext('2d');
  13.  
  14. context.beginPath();
  15.  
  16. context.moveTo(100, 20);
  17. context.quadraticCurveTo(230, 200, 250, 20);
  18.  
  19. context.lineWidth = 5;
  20. context.strokeStyle = "#0000ff";
  21. context.stroke();
  22.  
  23. </script>
  24.  
  25. </body>
  26. </html>

Пример 2: линии, связанные с квадратичной кривой

Исходный код:

context.beginPath();
context.moveTo(100, 20);
context.lineTo(200, 80);
context.quadraticCurveTo(230, 200, 250, 20);
context.lineTo(500, 90);
context.lineWidth = 5;
context.strokeStyle = "#0000ff";
context.stroke();

Пример 3: рисование изогнутой стрелки

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.   <meta charset="utf-8">
  5. <style>
  6. #myCanvas {
  7.   border: 1px solid black;
  8. }
  9. </style>
  10. </head>
  11. <body>
  12.   <canvas id="myCanvas" width="250" height="150"></canvas>
  13. <script>
  14. var canvas = document.querySelector('#myCanvas');
  15. var ctx = canvas.getContext('2d');
  16.  
  17. var contextX = 100;
  18. var contextY = 10;
  19. var endPointX = 200;
  20. var endPointY = 120;
  21. var controlPointX = 35;
  22. var controlPointY = 70;
  23.  
  24. drawCurvedArrow(contextX, contextY,
  25.                 endPointX, endPointY,
  26.                 controlPointX, controlPointY,
  27.                 3, // arrowWidth, попробуйте 30, например!
  28.                 20, // ширина стрелки, попробуйте меньшие значения, 10 ...
  29.                 'blue');
  30.  
  31.  
  32. function drawCurvedArrow(startPointX, startPointY,
  33.                          endPointX, endPointY,
  34.                          quadPointX, quadPointY,
  35.                          lineWidth,
  36.                          arrowWidth, 
  37.                          color) {
  38.   //ХОРОШАЯ ПРАКТИКА: функция меняет цвет и ширину линии -> сохранить контекст!
  39.   ctx.save();
  40.   ctx.strokeStyle = color;
  41.   ctx.lineWidth = lineWidth;
  42.  
  43.   // угол наклона касательной, используется для рисования наконечника стрелы
  44.   var arrowAngle = Math.atan2(quadPointX - endPointX, quadPointY - endPointY) + Math.PI;
  45.  
  46.   // начать новый путь
  47.   ctx.beginPath();
  48.   ctx.moveTo(startPointX, startPointY);
  49.  
  50.   ctx.quadraticCurveTo(quadPointX, quadPointY, endPointX, endPointY);
  51.  
  52.   ctx.moveTo(endPointX - (arrowWidth * Math.sin(arrowAngle - Math.PI / 6)), 
  53.              endPointY - (arrowWidth * Math.cos(arrowAngle - Math.PI / 6)));
  54.  
  55.   ctx.lineTo(endPointX, endPointY);
  56.  
  57.   ctx.lineTo(endPointX - (arrowWidth * Math.sin(arrowAngle + Math.PI / 6)), 
  58.              endPointY - (arrowWidth * Math.cos(arrowAngle + Math.PI / 6)));
  59.  
  60.   ctx.stroke();
  61.   ctx.closePath();
  62.  
  63.   // ХОРОШАЯ ПРАКТИКА -> восстановить контекст, как мы его сохранили в начале
  64.    // функции
  65.   ctx.restore();
  66. }
  67. </script>
  68. </body>
  69. </html>

Кривые Безье

Кривые Безье используются в основном для рисования S-образных фигур или асимметричных кривых.

(Фото взято с сайта  HTML5 Canvas Tutorials )

Кривые Безье определяются контекстной точкой, такой как квадратичные кривые, двумя контрольными точками, которые определяют две касательные, и конечной точкой. Первая часть кривой является касательной к воображаемой линии, определенной точкой контекста и первой контрольной точкой. Вторая часть кривой является касательной к воображаемой линии, определяемой второй контрольной точкой и конечной точкой.

Типичное использование кривых Безье

ctx.moveTo(contextX, contextY);
context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
// Необязательно: установить lineWidth и цвет обводки
context.lineWidth = 5;
context.strokeStyle = "#0000ff";
// рисовать!
ctx.stroke();

Пример 1

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.   <meta charset="utf-8">
  5. </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas1" height = 400 width="800">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11.     var canvas=document.querySelector('#myCanvas1');
  12.     var context=canvas.getContext('2d');
  13.  
  14.     context.beginPath();
  15.     context.moveTo(100, 20);
  16.     context.bezierCurveTo(290, -40, 200, 200, 400, 100);
  17.     context.lineWidth = 5;
  18.     context.strokeStyle = "#0000ff";
  19.     context.stroke();
  20.  
  21. </script>
  22.  
  23. </body>
  24. </html>

Пример 2: с прямыми, квадратичными и кривыми Безье

  1. <!DOCTYPE html>
  2. <html >
  3. <head>
  4.   <meta charset="utf-8">
  5. </head>
  6. <body>
  7.  
  8. <canvas id="myCanvas1" height = 400 width="800">Your browser does not support the canvas tag.</canvas>
  9.  
  10. <script type="text/javascript">
  11.     var canvas=document.querySelector('#myCanvas1');
  12.     var context=canvas.getContext('2d');
  13.  
  14.     context.beginPath();
  15.     context.moveTo(100, 20);
  16.     context.lineTo(200, 160);
  17.     context.quadraticCurveTo(230, 200, 250, 120);
  18.     context.bezierCurveTo(290, -40, 300, 200, 400, 150);
  19.     context.lineTo(500, 90);
  20.     context.closePath();
  21.     context.lineWidth = 5;
  22.     context.strokeStyle = "#0000ff";
  23.     context.stroke();
  24.  
  25. </script>
  26.  
  27. </body>
  28. </html>

В этом примере мы используем метод closePath (), чтобы нарисовать линию между последней точкой пути и первой точкой пути , чтобы рисунок выглядел как пара очков.

Обратите внимание, как разные части связаны между собой и составляют «путь»:

И напоследок: интересный интерактивный инструмент для генерации кода, который рисует кривые Безье (скриншот):

Попробуйте этот инструмент генерации кривых Безье онлайн!

Следующая статья «Цвета, градиенты, узоры, тени …»

Читайте больше по теме:

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
Просмотры: 767

Популярные записи