From 074785cea106179cb3305637055ab0a009ca74f2 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:42:52 -0500 Subject: initial commit --- .../api/canvas_api/a_basic_ray-caster/index.html | 46 ++ files/ru/web/api/canvas_api/index.html | 161 +++++ .../tutorial/advanced_animations/index.html | 376 +++++++++++ .../api/canvas_api/tutorial/basic_usage/index.html | 150 +++++ .../web/api/canvas_api/tutorial/finale/index.html | 51 ++ files/ru/web/api/canvas_api/tutorial/index.html | 58 ++ .../tutorial/optimizing_canvas/index.html | 120 ++++ .../pixel_manipulation_with_canvas/index.html | 267 ++++++++ .../canvas_api/tutorial/transformations/index.html | 275 ++++++++ .../index.html" | 333 ++++++++++ .../index.html" | 108 +++ .../index.html" | 308 +++++++++ .../index.html" | 726 +++++++++++++++++++++ .../index.html" | 166 +++++ .../index.html" | 582 +++++++++++++++++ 15 files changed, 3727 insertions(+) create mode 100644 files/ru/web/api/canvas_api/a_basic_ray-caster/index.html create mode 100644 files/ru/web/api/canvas_api/index.html create mode 100644 files/ru/web/api/canvas_api/tutorial/advanced_animations/index.html create mode 100644 files/ru/web/api/canvas_api/tutorial/basic_usage/index.html create mode 100644 files/ru/web/api/canvas_api/tutorial/finale/index.html create mode 100644 files/ru/web/api/canvas_api/tutorial/index.html create mode 100644 files/ru/web/api/canvas_api/tutorial/optimizing_canvas/index.html create mode 100644 files/ru/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html create mode 100644 files/ru/web/api/canvas_api/tutorial/transformations/index.html create mode 100644 "files/ru/web/api/canvas_api/tutorial/\320\270\321\201\320\277\320\276\320\273\321\214\320\267\320\276\320\262\320\260\320\275\320\270\320\265_\320\270\320\267\320\276\320\261\321\200\320\260\320\266\320\265\320\275\320\270\320\271/index.html" create mode 100644 "files/ru/web/api/canvas_api/tutorial/\320\272\320\276\320\274\320\277\320\276\320\267\320\270\321\206\320\270\320\270/index.html" create mode 100644 "files/ru/web/api/canvas_api/tutorial/\320\276\321\201\320\275\320\276\320\262\321\213_\320\260\320\275\320\270\320\274\320\260\321\206\320\270\320\270/index.html" create mode 100644 "files/ru/web/api/canvas_api/tutorial/\320\277\321\200\320\270\320\274\320\265\320\275\320\265\320\275\320\270\320\265_\321\201\321\202\320\270\320\273\320\265\320\271_\320\270_\321\206\320\262\320\265\321\202\320\276\320\262/index.html" create mode 100644 "files/ru/web/api/canvas_api/tutorial/\321\200\320\270\321\201\320\276\320\262\320\260\320\275\320\270\320\265_\321\202\320\265\320\272\321\201\321\202\320\260/index.html" create mode 100644 "files/ru/web/api/canvas_api/tutorial/\321\200\320\270\321\201\320\276\320\262\320\260\320\275\320\270\320\265_\321\204\320\270\320\263\321\203\321\200/index.html" (limited to 'files/ru/web/api/canvas_api') diff --git a/files/ru/web/api/canvas_api/a_basic_ray-caster/index.html b/files/ru/web/api/canvas_api/a_basic_ray-caster/index.html new file mode 100644 index 0000000000..72b21d9f5b --- /dev/null +++ b/files/ru/web/api/canvas_api/a_basic_ray-caster/index.html @@ -0,0 +1,46 @@ +--- +title: базовый ray-caster +slug: Web/API/Canvas_API/A_basic_ray-caster +translation_of: Web/API/Canvas_API/A_basic_ray-caster +--- +

{{CanvasSidebar}}

+ +

В этой статье представлен интересный реальный пример использования элемента {{HTMLElement("canvas")}} для выполнения программного рендеринга 3D-среды с помощью Ray-casting.

+ +

{{EmbedGHLiveSample("canvas-raycaster/index.html", 900, 300)}}

+ +

Открыть в новом окне

+ +

Зачем?

+ +

Я попробовал небольшой эксперимент, понимая, к моему восторгу, что стильный элемент <canvas> о котором я читал, поддерживается не только в Fierfox, но так же поддерживается последней версией Safari.

+ +

Хорошие обзор и руководство по canvas я нашел в MDN, но никто еще не писал об анимации, поэтому я решил попробовать базовый порт raycaster, над которым я работал некоторое время назад, и посмотреть, какую производительность мы можем ожидать от управляемого JavaScript-ом пиксельного буфера.

+ +

Как?

+ +

Основная идея заключается в использовании {{domxref("window.setInterval","setInterval()")}} с некоторой произвольной задержкой, соответствующей требуемой частоте кадров. После каждого интервала функция обновления будет перерисовывать холст, и отображать текущий вид. Я знаю, что мог бы начать с более простого примера, но я уверен, что руководство canvas доберется до этого, и я хотел посмотреть, смогу ли я это сделать.

+ +

Таким образом каждое обновление raycaster смотрит нажимали ли вы какие либо клавиши в последнее время, сохраняет расчеты и останавливается если вы бездествуете. Получив расчеты, холст очищается, земля и небо рисуются, положение камеры и/или ориентация обновляются, а лучи отбрасываются. Когда лучи попадают на стены, они отображают вертикальный кусок холста в цвете стены, на которую они попали, смешанный с более темной версией цвета в зависимости от расстояния до стены. Высота этого кусочка также моделируется расстоянием от камеры до стены и рисуется по центру линии горизонта.

+ +

Код, который я получил, - это смесь глав raycaster из старой книги Андре Ламотетрикса о гуру программирования игр (ISBN: 0672305070) и  java raycaster, которую я нашел в интернете, отфильтровал, переименовал, и внес все изменения, которые нужно было внести, чтобы все работало хорошо.

+ +

Результаты

+ +

Холст в Safari 2.0.1 выполнен на удивление хорошо. С коэффициентом блочности, увеличенным до отображения кусочка шириной 8 пикселей, я могу запустить окно 320 x 240 при 24 fps на моем Apple mini. Firefox 1.5 Beta 1 еще быстрее; я могу запускать 320 x 240 при 24 fps с 4 пиксельным кусочком. Не совсем новый член семейства програмного обеспечения ID, но довольно приличный, учитывая, что это полностью интерпретируемая среда, и мне не нужно было беспокоиться о выделении памяти или видеорежимах или кодировании внутренних процедур в ассемблере или чем-то еще. Код получился очень эффективным, он использует поиск по массиву предварительно вычисленных значений, но я не гуру оптимизации, поэтому все, вероятно, можно было бы написать быстрее.

+ +

Кроме того, он оставляет желать лучшего с точки зрения попыток быть игровым движком—нет текстур стен, нет спрайтов, нет дверей, даже нет телепортов, чтобы добраться до другого уровня. Но я уверен, что все эти вещи могут быть добавлены, через некоторое время. Canvas API поддерживает пиксельное копирование изображений, поэтому текстуры могут быть добавлены. Я оставлю это для другой статьи, возможно, от другого человека. =)

+ +

ray-caster

+ +

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

+ +

Так что запустите Safari 1.3+ или Firefox 1.5+ или какой-нибудь другой браузер, который поддерживает элемент <canvas> и наслаждайтесь!

+ +

input.js | Level.js | Player.js | RayCaster.html | RayCaster.js | trace.css | trace.js

+ +

Смотрите также

+ + diff --git a/files/ru/web/api/canvas_api/index.html b/files/ru/web/api/canvas_api/index.html new file mode 100644 index 0000000000..e3986f8d75 --- /dev/null +++ b/files/ru/web/api/canvas_api/index.html @@ -0,0 +1,161 @@ +--- +title: Canvas +slug: Web/API/Canvas_API +tags: + - API + - Canvas + - Обзор + - Справка +translation_of: Web/API/Canvas_API +--- +
{{CanvasSidebar}}
+ +

Элемент {{HTMLElement("canvas")}}, добавленный в HTML5, предназначен для создания графики с помощью JavaScript. Например, его используют для рисования графиков, создания фотокомпозиций, анимаций и даже обработки и рендеринга видео в реальном времени.

+ +

«Canvas»  в переводе с английского означает «холст».

+ +

Приложения от Mozilla поддерживают <canvas> начиная с Gecko 1.8 (т.е. с Firefox 1.5). Этот элемент первоначально был представлен Apple для OS X Dashboard и Safari. Internet Explorer поддерживает <canvas> начиная с 9 версии; для более ранних версий IE поддержку для <canvas> можно добавить с помощью скрипта из проекта Google's Explorer Canvas. Google Chrome и Opera 9 также поддерживают <canvas>.

+ +

Элемент <canvas> также используется технологией WebGL для отрисовки аппаратно-ускоренной 3D-графики на вебстраницах.

+ +

Пример

+ +

Это простой пример использования {{domxref("CanvasRenderingContext2D.fillRect()")}} метода.

+ +

HTML

+ +
<canvas id="canvas"></canvas>
+
+ +

JavaScript

+ +
var canvas = document.getElementById("canvas");
+var ctx = canvas.getContext("2d");
+
+ctx.fillStyle = "green";
+ctx.fillRect(10, 10, 100, 100);
+
+ +

Отредактируйте код ниже, чтобы увидеть результат на холсте.

+ + + +

{{ EmbedLiveSample('Playable_code', 700, 360) }}

+ +

Справочные материалы

+ +
+ +
+ +

Интерфейсы, связанные с WebGLRenderingContext, ссылаются на WebGL.

+ +

Руководства

+ +
+
Canvas tutorial
+
Подробный учебник, охватывающий как основное использование <canvas>, так и его расширенные функции.
+
Фрагменты кода: Canvas
+
Некоторые фрагменты кода, ориентированные на разработчиков, с использованием <canvas>.
+
Demo: A basic ray-caster
+
Демо анимации трассировки-лучей используя canvas.
+
Drawing DOM objects into a canvas
+
Как рисовать DOM контент, таких как HTML элементы, в canvas.
+
Manipulating video using canvas
+
Объединяет {{HTMLElement("video")}} и {{HTMLElement("canvas")}} для манипулирования видео данных в реальном времени.
+
+ +

Ресурсы

+ +

Основное

+ + + +

Библиотеки

+ + + +

Спецификации

+ + + + + + + + + + + + + + + + +
СпецификацияСтатусКомментарий
{{SpecName('HTML WHATWG', "the-canvas-element.html", "Canvas")}}{{Spec2('HTML WHATWG')}} 
+ +

См.также

+ + diff --git a/files/ru/web/api/canvas_api/tutorial/advanced_animations/index.html b/files/ru/web/api/canvas_api/tutorial/advanced_animations/index.html new file mode 100644 index 0000000000..f9dc3f7a3e --- /dev/null +++ b/files/ru/web/api/canvas_api/tutorial/advanced_animations/index.html @@ -0,0 +1,376 @@ +--- +title: Расширенные анимации +slug: Web/API/Canvas_API/Tutorial/Advanced_animations +translation_of: Web/API/Canvas_API/Tutorial/Advanced_animations +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_animations", "Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas")}}
+ +
+

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

+
+ +

Рисование мяча

+ +

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

+ +
<canvas id="canvas" width="600" height="300"></canvas>
+
+ +

Как обычно, нам нужен контекст рисования. Чтобы нарисовать шар, мы создадим объект ball, который содержит свойства и метод draw() , чтобы нарисовать его на canvas.

+ +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+var ball = {
+  x: 100,
+  y: 100,
+  radius: 25,
+  color: 'blue',
+  draw: function() {
+    ctx.beginPath();
+    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
+    ctx.closePath();
+    ctx.fillStyle = this.color;
+    ctx.fill();
+  }
+};
+
+ball.draw();
+ +

Здесь нет ничего особенного, шар на самом деле представляет собой простой круг и рисуется с помощью метода {{domxref("CanvasRenderingContext2D.arc()", "arc()")}}.

+ +

Добавление скорости

+ +

Теперь, когда у нас есть шар, мы готовы добавить базовую анимацию, как мы узнали из последней главы этого урока. Опять же, {{domxref("window.requestAnimationFrame()")}} помогает нам контролировать анимацию. Мяч перемещается, добавляя вектор скорости в положение. Для каждого кадра мы также {{domxref("CanvasRenderingContext2D.clearRect", "очищаем", "", 1)}} холст, чтобы удалить старые круги из предыдущих кадров.

+ +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+var raf;
+
+var ball = {
+  x: 100,
+  y: 100,
+  vx: 5,
+  vy: 2,
+  radius: 25,
+  color: 'blue',
+  draw: function() {
+    ctx.beginPath();
+    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
+    ctx.closePath();
+    ctx.fillStyle = this.color;
+    ctx.fill();
+  }
+};
+
+function draw() {
+  ctx.clearRect(0,0, canvas.width, canvas.height);
+  ball.draw();
+  ball.x += ball.vx;
+  ball.y += ball.vy;
+  raf = window.requestAnimationFrame(draw);
+}
+
+canvas.addEventListener('mouseover', function(e) {
+  raf = window.requestAnimationFrame(draw);
+});
+
+canvas.addEventListener('mouseout', function(e) {
+  window.cancelAnimationFrame(raf);
+});
+
+ball.draw();
+
+ +

Границы

+ +

Без какого-либо граничного коллизионного тестирования наш мяч быстро выбегает из холста. Нам нужно проверить, не находятся ли x и y положения шара вне размеров холста и не инвертируют направление векторов скорости. Для этого мы добавим следующие проверки в метод draw :

+ +
if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
+  ball.vy = -ball.vy;
+}
+if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
+  ball.vx = -ball.vx;
+}
+ +

Первое демо

+ +

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

+ + + +

{{EmbedLiveSample("First_demo", "610", "310")}}

+ +

Ускорение

+ +

Чтобы сделать движение более реальным, вы можете играть со скоростью, нпример так:

+ +
ball.vy *= .99;
+ball.vy += .25;
+ +

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

+ + + +

{{EmbedLiveSample("Second_demo", "610", "310")}}

+ +

Скользящий эффект

+ +

До сих пор мы использовали метод {{domxref("CanvasRenderingContext2D.clearRect", "clearRect")}}, когда очищали предыдущий кадр. Если заменить этот метод на {{domxref("CanvasRenderingContext2D.fillRect", "fillRect")}} с полу-прозрачным стилем, можно легко создать эффект скольжения.

+ +
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
+ctx.fillRect(0, 0, canvas.width, canvas.height);
+ + + +

{{EmbedLiveSample("Third_demo", "610", "310")}}

+ +

Добавление управления мышью

+ +

Чтобы получить некоторый контроль над мячом, мы можем заставить его следовать за нашей мышью, например, с помощью события mousemove . Событие click отпускает мяч и позволяет ему снова прыгать

+ + + +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+var raf;
+var running = false;
+
+var ball = {
+  x: 100,
+  y: 100,
+  vx: 5,
+  vy: 1,
+  radius: 25,
+  color: 'blue',
+  draw: function() {
+    ctx.beginPath();
+    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
+    ctx.closePath();
+    ctx.fillStyle = this.color;
+    ctx.fill();
+  }
+};
+
+function clear() {
+  ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
+  ctx.fillRect(0,0,canvas.width,canvas.height);
+}
+
+function draw() {
+  clear();
+  ball.draw();
+  ball.x += ball.vx;
+  ball.y += ball.vy;
+
+  if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
+    ball.vy = -ball.vy;
+  }
+  if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
+    ball.vx = -ball.vx;
+  }
+
+  raf = window.requestAnimationFrame(draw);
+}
+
+canvas.addEventListener('mousemove', function(e) {
+  if (!running) {
+    clear();
+    ball.x = e.clientX;
+    ball.y = e.clientY;
+    ball.draw();
+  }
+});
+
+canvas.addEventListener('click', function(e) {
+  if (!running) {
+    raf = window.requestAnimationFrame(draw);
+    running = true;
+  }
+});
+
+canvas.addEventListener('mouseout', function(e) {
+  window.cancelAnimationFrame(raf);
+  running = false;
+});
+
+ball.draw();
+
+ +

Переместите шар с помощью мыши и отпустите его одним щелчком.

+ +

{{EmbedLiveSample("Adding_mouse_control", "610", "310")}}

+ +

Breakout(арканоид)

+ +

В этой короткой главе описаны некоторые приемы создания продвинутой анимации.  Как насчет того, что бы добавить доску, кирпичи и превратить это демо в игру Breakout(в Росси более известный клон этой игры - арканоид)? Посетите Game development чтобы узнать больше об играх.

+ +

Смотрите так же

+ + + +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_animations", "Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas")}}

diff --git a/files/ru/web/api/canvas_api/tutorial/basic_usage/index.html b/files/ru/web/api/canvas_api/tutorial/basic_usage/index.html new file mode 100644 index 0000000000..3c32d75985 --- /dev/null +++ b/files/ru/web/api/canvas_api/tutorial/basic_usage/index.html @@ -0,0 +1,150 @@ +--- +title: Базовое использование Canvas +slug: Web/API/Canvas_API/Tutorial/Basic_usage +tags: + - Canvas +translation_of: Web/API/Canvas_API/Tutorial/Basic_usage +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial", "Web/API/Canvas_API/Tutorial/Drawing_shapes")}}
+ +

Давайте начнем этот урок с изучения элемента {{HTMLElement("canvas")}} как такового. В конце этой страницы Вы узнаете как устанавливать canvas 2D context и нарисуете первый пример в вашем браузере.

+ +

Элемент <canvas>

+ +
<canvas id="tutorial" width="150" height="150"></canvas>
+
+ +

Он выглядит как элемент <img>, но его отличие в том, что он не имеет атрибутов src и alt. Элемент <canvas> имеет только два атрибута - ширину и высоту. Оба они не обязательны и могут быть выставлены с использованием свойств DOM. Если атрибуты высоты и ширины не установлены, canvas будет по умолчанию шириной 300 пикселей и в высоту 150 пикселей. Вы так же можете выставить размеры произвольно в CSS, но во время рендеринга изображение будет масштабироваться в соответствии с его размером и ориентацией.

+ +
+

Примечание: Если вид вашего изображения кажется вам искаженным, попробуйте указать атрибуты ширины и высоты в явном виде в атрибутах <canvas> , а не с помощью CSS.

+
+ +

Атрибут id не специфичен для элемента <canvas>, но он может быть применен по умолчанию в атрибутах HTML, так как он может быть использован (почти) для любого элемента HTML (так же как класс). Это всегда отличная идея использовать id, так как это позволяет намного проще идентифицировать наш элемент в сценарии.

+ +

Элемент <canvas> может быть стилизован также, как любое изображение (margin, border, background, и т. д.). Эти правила, как бы то ни было, фактически не влияют на отрисовку в canvas. Мы увидим как это сделано позже в этом руководстве. Когда стили не указаны, canvas будет по умолчанию абсолютно прозрачным.

+ +
+

Запасное содержимое

+ +

Из-за того, что старые браузеры (в особенности, версии Internet Explorer раннее чем 9) не поддерживают элемент {{HTMLElement("canvas")}}, Вам следует предоставить запасное содержимое для отображения этими браузерами.

+ +

Это очень просто: мы всего лишь предоставляем альтернативное содержимое внутри элемента <canvas>. Браузеры, которые не поддерживают <canvas> проигнорируют container и отобразят запасное содержимое этого тега. Браузеры, которые поддерживают <canvas> проигнорируют запасное содержимое, и просто нормально отобразят canvas.

+ +

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

+ +
<canvas id="stockGraph" width="150" height="150">
+  current stock price: $3.15 +0.15
+</canvas>
+
+<canvas id="clock" width="150" height="150">
+  <img src="images/clock.png" width="150" height="150" alt=""/>
+</canvas>
+
+ +

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

+ +

Требуется тег </canvas>

+ +

В отличии от элемента {{HTMLElement("img")}}, элемент {{HTMLElement("canvas")}}  требует закрывающийся тег (</canvas>). Если этот тег не предоставлен, остаток документа будет считаться запасным контентом и не будет отображен.

+ +

Если запасной контент не нужен, простой  <canvas id="foo" ...></canvas> полностью совместим со всеми браузерами, что поддерживают canvas.

+ +

Рендеринг содержимого (контекста)

+ +

Элемент {{HTMLElement("canvas")}} в документе создается с фиксированным размером элемента для рисования, который может иметь один или несколько контекстов для рендеринга, создавая и манипулируя содержимым для показа. В данном руководстве мы сфокусируемся на 2D рендеринге. Другие контексты могут предоставлять разные типы рендеринга, к примеру WebGl использует 3D контекст основанный на OpenGL ES.

+ +

Холст изначально пустой и прозрачный. Первым делом скрипт получает доступ к контексту и отрисовывает его. Элемент {{HTMLElement("canvas")}} имеет метод getContext(), используется для получения контекста визуализации и ее функции рисования. getContext() принимает один параметр, тип контекста. Для 2D графики, которая охвачена этим руководством будем использвать метку "2d".

+ +
var canvas = document.getElementById('tutorial');
+var ctx = canvas.getContext('2d');
+
+ +

В первой строке скрипта мы получаем узел нашего холста {{HTMLElement("canvas")}}, далее с помощью  {{domxref("document.getContext()")}} метода мы присваиваем ему контекст. Имея узел элемента , вы всегда можете получить для рисования контекст при помощи метода getContext().

+ +
+

Проверка поддержки

+ +

Ранее уже упоминалось, что в браузерах, которые не поддерживают {{HTMLElement("canvas")}} отображается запасное содержимое. Но помимо этого, определить, поддерживает ли браузер canvas, можно прямо из кода, проверив наличие метода getContext(). Код с запасным содержимым, который мы приводили Выше, становится следующим:

+ +
var canvas = document.getElementById('tutorial');
+
+if (canvas.getContext){
+  var ctx = canvas.getContext('2d');
+  // drawing code here
+} else {
+  // canvas-unsupported code here
+}
+
+
+
+ +

Скелет шаблона

+ +

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

+ +
+

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

+
+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <title>Canvas tutorial</title>
+    <script type="text/javascript">
+      function draw(){
+        var canvas = document.getElementById('tutorial');
+        if (canvas.getContext){
+          var ctx = canvas.getContext('2d');
+        }
+      }
+    </script>
+    <style type="text/css">
+      canvas { border: 1px solid black; }
+    </style>
+  </head>
+  <body onload="draw();">
+    <canvas id="tutorial" width="150" height="150"></canvas>
+  </body>
+</html>
+
+ +

Скрипт вызывает функцию draw(), которая выполнится, когда страница закончит загрузку. Это делается путем события {{event("load")}} в документе. Эта функция может быть вызвана как единожды, так и с помощью данных методов {{domxref("window.setTimeout()")}}, {{domxref("window.setInterval()")}}, или любым другим обработчиком события, когда страница будет загружена.

+ +

Вот как шаблон будет выглядеть в действии.

+ +

{{EmbedLiveSample("Скелет_шаблона", 160, 160)}}

+ +

Простой пример

+ +

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

+ +
<!DOCTYPE html>
+<html>
+ <head>
+  <script type="application/javascript">
+    function draw() {
+      var canvas = document.getElementById("canvas");
+      if (canvas.getContext) {
+        var ctx = canvas.getContext("2d");
+
+        ctx.fillStyle = "rgb(200,0,0)";
+        ctx.fillRect (10, 10, 55, 50);
+
+        ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
+        ctx.fillRect (30, 30, 55, 50);
+      }
+    }
+  </script>
+ </head>
+ <body onload="draw();">
+   <canvas id="canvas" width="150" height="150"></canvas>
+ </body>
+</html>
+
+ +

Этот пример выглядит так:

+ +

{{EmbedLiveSample("Простой_пример", 160, 160, "https://mdn.mozillademos.org/files/228/canvas_ex1.png")}}

+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial", "Web/API/Canvas_API/Tutorial/Drawing_shapes")}}

diff --git a/files/ru/web/api/canvas_api/tutorial/finale/index.html b/files/ru/web/api/canvas_api/tutorial/finale/index.html new file mode 100644 index 0000000000..63187795bd --- /dev/null +++ b/files/ru/web/api/canvas_api/tutorial/finale/index.html @@ -0,0 +1,51 @@ +--- +title: Finale +slug: Web/API/Canvas_API/Tutorial/Finale +translation_of: Web/API/Canvas_API/Tutorial/Finale +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Optimizing_canvas")}}
+ +
+

Поздравляем! Вы завершили Canvas tutorial! Эти знания помогут вам создавать великолепную 2d графику в сети.

+
+ +

Больше примеров и учебных материалов

+ +

Различные демки и дополнительные разъяснеия о canvas расположены на этих сайтах:

+ +
+
Codepen.io
+
Песочница для фронтенд разработчика и редактор кода в браузере.
+
Canvasdemos.com
+
Приложения, игры, инструменты и учебные пособия для HTML5 canvas элементов.
+
HTML5CanvasTutorials
+
Примеры для большинтсва canvas APIs.
+
31 days of Canvas tutorials
+
Прекрасное введение в разработку графики на JavaScript.
+
Game development
+
Игры - одна из наболее популярных  действий на компьютере. Постоянно появляются новые технологии, делающих возможным разработку более лучших и мощных игр, чем те, которые могли быть созданы стандартными средствами веб браузеров.
+
+ +

Другие Web API

+ +

Эти APIs могут быть полезны  в дальнейшей работе с canvas и графикой:

+ +
+
WebGL
+
API для отображения интерактивной 3D графики.
+
SVG
+
Масштабируемая Векторная Графика (Scalable Vector Graphics) позволяет описывать картинки в виде набора векторов (линий) и  форм, позволяюших плавно изменять размер вне зависимости от размера в котором они нарисованы.
+
Web Audio
+
Web Audio API представляет мощную и универсальную систему контроля аудио в сети, позволяющая разработчикам получить аудио ресурсы, добавлять эффекты аудио, создавать аудио визуализацию,  применять пространственные эффекты (такие как сдвиг) и многое другое.
+
+ +

Вопросы

+ +
+
Stackoverflow
+
Вопросы с тегом "canvas".
+
Comments about this tutorial – the MDN documentation community
+
Если у вас остались вопросы об этом учебнике или вы хотите поблагодарить нас, не стесняйтесь обращаться к нам!
+
+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Optimizing_canvas")}}

diff --git a/files/ru/web/api/canvas_api/tutorial/index.html b/files/ru/web/api/canvas_api/tutorial/index.html new file mode 100644 index 0000000000..8409367956 --- /dev/null +++ b/files/ru/web/api/canvas_api/tutorial/index.html @@ -0,0 +1,58 @@ +--- +title: Руководство по Canvas +slug: Web/API/Canvas_API/Tutorial +tags: + - Canvas + - Graphics +translation_of: Web/API/Canvas_API/Tutorial +--- +

{{CanvasSidebar}}

+ +

<canvas> — это HTML элемент, использующийся для рисования графики средствами языков программирования (обычно это JavaScript). Он может, к примеру, использоваться для рисования графов, создания коллажей или простой (и не очень) анимации.
+ Изображения в правой части статьи являются примерами использования <canvas>.
+ Примеры их создания приводятся в этой статье. 

+ +

В этом руководстве описываются основы использования элемента <canvas> для рисования 2D-графики. Приведенные примеры дадут вам понимание того, что вы можете сделать с помощью <canvas>, а использованные в статье фрагменты кода помогут в создании собственных проектов.

+ +

Впервые <canvas> использовался компанией Apple для создания Mac OS X Dashboard, а затем был реализован в Web-браузерах. На сегодняшний день все основные браузеры поддерживют работу с <canvas>. Тег <canvas> часть спецификации WhatWG Web applications 1.0 также известной как HTML5.

+ +

Прежде чем начать

+ +

Работа с элементом <canvas> довольно проста, но потребует от вас базовых знаний HTML и JavaScript. Следует предупредить о том, что элемент <canvas> не работает в некоторых старых браузерах, но поддерживается большинством современных браузеров. Стандартный размер <canvas>  300px × 150px (ширина × высота), однако эти размеры могут быть изменены при помощи HTML-атрибутов height и width. Для рисования графики <canvas> мы будем использовать javascript context object,  который создает графику динамически.

+ +

В этом руководстве

+ + + +

Смотрите Также

+ + + +

Примечание для помощников

+ +

Из-за досадной технической ошибки, которая произошла 17 июня 2013 года, мы утеряли историю по этому руководству, в том числе атрибуции ко всем прошлым, участвовавшие в его создании. Мы приносим извинения за это, и надеемся, что вы простите нас за этот несчастный случай. 

+ +
+
{{ Next("Web/API/Canvas_API/Tutorial/Basic_usage") }}
+
diff --git a/files/ru/web/api/canvas_api/tutorial/optimizing_canvas/index.html b/files/ru/web/api/canvas_api/tutorial/optimizing_canvas/index.html new file mode 100644 index 0000000000..40a83917e8 --- /dev/null +++ b/files/ru/web/api/canvas_api/tutorial/optimizing_canvas/index.html @@ -0,0 +1,120 @@ +--- +title: Оптимизация canvas +slug: Web/API/Canvas_API/Tutorial/Optimizing_canvas +tags: + - Canvas + - Graphics + - HTML + - HTML5 + - Tutorial + - Оптимизация + - Урок + - Холст +translation_of: Web/API/Canvas_API/Tutorial/Optimizing_canvas +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility", "Web/API/Canvas_API/Tutorial/Finale")}}
+ +
+

Элемент {{HTMLElement ("canvas")}} является одним из наиболее широко используемых инструментов для рендеринга 2D-графики в Интернете. Однако когда веб-сайты и приложения используют Canvas API на пределе его возможностей, производительность начинает снижаться. В этой статье приводятся предложения по оптимизации использования элемента canvas для обеспечения хорошей работы графики.

+
+ +

Советы по производительности

+ +

Ниже приведен сборник советов по улучшению производительности canvas.

+ +

Предварительно отрисуйте похожие примитивы или повторяющиеся объекты на offscreen canvas

+ +

Если вы повторяете одни и те же операции рисования в каждом кадре анимации, рассмотрите возможность их выгрузки на offscreen canvas. Затем вы можете визуализировать закадровое изображение на свой основной canvas так часто, как это необходимо, без необходимости повторять шаги, необходимые для его генерации.

+ +
myCanvas.offscreenCanvas = document.createElement('canvas');
+myCanvas.offscreenCanvas.width = myCanvas.width;
+myCanvas.offscreenCanvas.height = myCanvas.height;
+
+myCanvas.getContext('2d').drawImage(myCanvas.offScreenCanvas, 0, 0);
+
+ +

Избегайте координат с плавающей точкой и используйте вместо них целые числа

+ +

Субпиксельный рендеринг происходит при рендеринге объектов на canvas без целых значений.

+ +
ctx.drawImage(myImage, 0.3, 0.5);
+
+ +

Это заставляет браузер выполнять дополнительные вычисления для создания эффекта сглаживания. Чтобы избежать этого, обязательно округлите все координаты, используемые в вызовах {{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}}, например, с помощью {{jsxref("Math.floor()")}}.

+ +

Не масштабируйте изображения в drawImage

+ +

При загрузке кэшируйте изображения разных размеров на offscreen canvas, а не постоянно масштабируйте их в {{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}}.

+ +

Используйте несколько слоев canvas для сложных сцен

+ +

Вы можете обнаружить, что некоторые объекты в вашем приложении нужно часто перемещать или менять, в то время как другие остаются относительно статичными. Возможной оптимизацией в этой ситуации является наложение ваших элементов с использованием нескольких элементов <canvas>.

+ +

Например, допустим, у вас есть игра с пользовательским интерфейсом наверху, геймплеем в середине и статическим фоном внизу. В этом случае вы можете разбить свою игру на три слоя <canvas>. Пользовательский интерфейс будет меняться только при изменении пользователем, слой с игровым процессом будет меняться с каждым новым кадром, а фон останется в основном неизменным.

+ +
<div id="stage">
+  <canvas id="ui-layer" width="480" height="320"></canvas>
+  <canvas id="game-layer" width="480" height="320"></canvas>
+  <canvas id="background-layer" width="480" height="320"></canvas>
+</div>
+
+<style>
+  #stage {
+    width: 480px;
+    height: 320px;
+    position: relative;
+    border: 2px solid black;
+  }
+
+  canvas { position: absolute; }
+  #ui-layer { z-index: 3; }
+  #game-layer { z-index: 2; }
+  #background-layer { z-index: 1; }
+</style>
+
+ +

Используйте простой CSS для больших фоновых изображений

+ +

Если у вас есть статическое фоновое изображение, вы можете нарисовать его на простом элементе {{HTMLElement("div")}}, используя свойство CSS {{cssxref("background")}}, и расположить его под canvas. Это сведет на нет необходимость рендеринга фона на canvas на каждом тике.

+ +

Масштабирование холста с использованием CSS-преобразований

+ +

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

+ +
var scaleX = window.innerWidth / canvas.width;
+var scaleY = window.innerHeight / canvas.height;
+
+var scaleToFit = Math.min(scaleX, scaleY);
+var scaleToCover = Math.max(scaleX, scaleY);
+
+stage.style.transformOrigin = '0 0'; //scale from top left
+stage.style.transform = 'scale(' + scaleToFit + ')';
+
+ +

Отключите прозрачность

+ +

Если ваше приложение использует canvas и не нуждается в прозрачном фоне, установите для параметра alphaзначение false при создании контекста рисования с помощью {{domxref("HTMLCanvasElement.getContext()")}}. Эта информация может использоваться браузером для оптимизации рендеринга.

+ +
var ctx = canvas.getContext('2d', { alpha: false });
+ +

Другие советы

+ + + +

Смотрите также

+ + + +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility", "Web/API/Canvas_API/Tutorial/Finale")}}

diff --git a/files/ru/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html b/files/ru/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html new file mode 100644 index 0000000000..8b8653a9b3 --- /dev/null +++ b/files/ru/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html @@ -0,0 +1,267 @@ +--- +title: Пиксельная манипуляция с холстом +slug: Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas +tags: + - Графика + - Промежуточный + - Руководство + - Холст +translation_of: Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Advanced_animations", "Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility")}}
+ +
+

До сих пор мы не смотрели на фактические пиксели нашего объекта canvas (далее "холст"). С объектом ImageData вы можете напрямую читать и писать массив данных для управления пиксельными данными. Мы также рассмотрим, как можно сгладить сглаживание изображения (сглаживание) и как сохранить изображения с вашего холста.

+
+ +

Объект ImageData

+ +

Объект {{domxref ("ImageData")}} представляет базовые пиксельные данные области объекта холста. Он содержит следующие атрибуты только для чтения:

+ +
+
width
+
Ширина изображения в пикселях.
+
height
+
Высота изображения в пикселях.
+
data
+
A {{jsxref ("Uint8ClampedArray")}} представляет собой одномерный массив, содержащий данные в порядке RGBA, с целыми значениями от 0 до 255 (в комплекте).
+
+ +

Свойство data возвращает {{jsxref ("Uint8ClampedArray")}}, к которому можно получить доступ, чтобы посмотреть на необработанные пиксельные данные; каждый пиксель представлен четырьмя однобайтовыми значениями (красный, зеленый, синий и альфа в этом порядке, то есть формат «RGBA»). Каждый компонент цвета представлен целым числом от 0 до 255. Каждому компоненту присваивается последовательный индекс внутри массива, причем красный компонент верхнего левого пикселя находится в индексе 0 внутри массива. Затем пиксели идут слева направо, затем вниз, по всему массиву.

+ +

{{Jsxref ("Uint8ClampedArray")}} содержит высоту × ширину × 4 байта данных, значения индекса варьируются от 0 до (высота × ширина × 4) -1.

+ +

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

+ +
blueComponent = imageData.data[((50 * (imageData.width * 4)) + (200 * 4)) + 2];
+ +

Вы можете получить доступ к размеру массива пикселей в байтах, прочитав атрибут Uint8ClampedArray.length:

+ +
var numBytes = imageData.data.length;
+
+ +

Создание объекта ImageData

+ +

Чтобы создать новый пустой объект ImageData , вы должны использовать метод {{domxref ("CanvasRenderingContext2D.createImageData", "createImageData ()")}}. Существуют две версии метода createImageData() :

+ +
var myImageData = ctx.createImageData(width, height);
+ +

Это создает новый объект ImageData с указанными параметрами. Все пиксели заданы прозрачным черным.

+ +

Вы также можете создать новый объект ImageData ImageData с теми же размерами, что и объект, заданный anotherImageData . Все пиксели нового объекта установлены на прозрачный черный. Это не копирует данные изображения!

+ +
var myImageData = ctx.createImageData(anotherImageData);
+ +

Получение пиксельных данных для контекста

+ +

Чтобы получить объект ImageData , содержащий копию пиксельных данных для контекста холста, вы можете использовать метод getImageData() :

+ +
var myImageData = ctx.getImageData(left, top, width, height);
+ +

Этот метод возвращает объект ImageData , представляющий пиксельные данные для области холста, углы которого представлены точками (left , top), (left+width , top), (left , top+height) и (left+width , top+height). Координаты задаются в единицах пространства координат холста.

+ +
+

Примечание: Любые пиксели за пределами холста возвращаются как прозрачный черный цвет в результирующий объект ImageData .

+
+ +

Этот метод также показан в статье Manipulating video using canvas.

+ +

Выбор цвета

+ +

В этом примере мы используем метод getImageData() для отображения цвета под курсором мыши. Для этого нам нужна текущая позиция мыши с layerX и layerY, затем мы просматриваем пиксельные данные в этой позиции в массиве пикселей, который предоставляет нам getImageData() . Наконец, мы используем данные массива для установки цвета фона и текста <div> для отображения цвета.

+ + + +
var img = new Image();
+img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
+var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+img.onload = function() {
+  ctx.drawImage(img, 0, 0);
+  img.style.display = 'none';
+};
+var color = document.getElementById('color');
+function pick(event) {
+  var x = event.layerX;
+  var y = event.layerY;
+  var pixel = ctx.getImageData(x, y, 1, 1);
+  var data = pixel.data;
+  var rgba = 'rgba(' + data[0] + ', ' + data[1] +
+             ', ' + data[2] + ', ' + (data[3] / 255) + ')';
+  color.style.background =  rgba;
+  color.textContent = rgba;
+}
+canvas.addEventListener('mousemove', pick);
+
+ +

{{ EmbedLiveSample('A_color_picker', 610, 240) }}

+ +

Отображение пиксельных данных в контекст

+ +

Вы можете использовать метод putImageData() для рисования пиксельных данных в контексте:

+ +
ctx.putImageData(myImageData, dx, dy);
+
+ +

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

+ +

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

+ +
ctx.putImageData(myImageData, 0, 0);
+
+ +

Оттенки серого цвета и инвертирование цветов

+ +

В этом примере мы перебираем все пиксели для изменения их значений, а затем помещаем модифицированный массив пикселей обратно в canvas с помощью putImageData(). Функция инвертирования просто вычитает каждый цвет из максимального значения 255. Функция оттенков серого просто использует среднее значение красного, зеленого и синего. Вы также можете использовать средневзвешенное значение, заданное формулой x = 0.299r + 0.587g + 0.114b, например. Для дополнительной информации см. Grayscale в Википедии.

+ + + +
var img = new Image();
+img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
+img.onload = function() {
+  draw(this);
+};
+
+function draw(img) {
+  var canvas = document.getElementById('canvas');
+  var ctx = canvas.getContext('2d');
+  ctx.drawImage(img, 0, 0);
+  img.style.display = 'none';
+  var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+  var data = imageData.data;
+
+  var invert = function() {
+    for (var i = 0; i < data.length; i += 4) {
+      data[i]     = 255 - data[i];     // red
+      data[i + 1] = 255 - data[i + 1]; // green
+      data[i + 2] = 255 - data[i + 2]; // blue
+    }
+    ctx.putImageData(imageData, 0, 0);
+  };
+
+  var grayscale = function() {
+    for (var i = 0; i < data.length; i += 4) {
+      var avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
+      data[i]     = avg; // red
+      data[i + 1] = avg; // green
+      data[i + 2] = avg; // blue
+    }
+    ctx.putImageData(imageData, 0, 0);
+  };
+
+  var invertbtn = document.getElementById('invertbtn');
+  invertbtn.addEventListener('click', invert);
+  var grayscalebtn = document.getElementById('grayscalebtn');
+  grayscalebtn.addEventListener('click', grayscale);
+}
+
+ +

{{ EmbedLiveSample('Grayscaling_and_inverting_colors', 330, 270) }}

+ +

Масштабирование и сглаживание

+ +

С помощью метода                                                             {{domxref ("CanvasRenderingContext2D.drawImage", "drawImage ()")}}, второго холста и свойства {{domxref ("CanvasRenderingContext2D.imageSmoothingEnabled", "imageSmoothingEnabled")}} мы способны увеличить изображение и посмотреть его более детально.

+ +

Мы получаем положение мыши и обрезаем изображение на 5 пикселей левее и выше и на 5 пикселей правее и ниже положения мыши. Затем мы копируем его на другой холст и изменяем размер изображения до размера, который мы хотим. При масштабировании мы изменяем холст с исходного размера 10×10 пикселей до 200×200.

+ +
zoomctx.drawImage(canvas,
+                  Math.abs(x - 5), Math.abs(y - 5),
+                  10, 10, 0, 0, 200, 200);
+ +

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

+ + + + + +
var img = new Image();
+img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
+img.onload = function() {
+  draw(this);
+};
+
+function draw(img) {
+  var canvas = document.getElementById('canvas');
+  var ctx = canvas.getContext('2d');
+  ctx.drawImage(img, 0, 0);
+  img.style.display = 'none';
+  var zoomctx = document.getElementById('zoom').getContext('2d');
+
+  var smoothbtn = document.getElementById('smoothbtn');
+  var toggleSmoothing = function(event) {
+    zoomctx.imageSmoothingEnabled = this.checked;
+    zoomctx.mozImageSmoothingEnabled = this.checked;
+    zoomctx.webkitImageSmoothingEnabled = this.checked;
+    zoomctx.msImageSmoothingEnabled = this.checked;
+  };
+  smoothbtn.addEventListener('change', toggleSmoothing);
+
+  var zoom = function(event) {
+    var x = event.layerX;
+    var y = event.layerY;
+    zoomctx.drawImage(canvas,
+                      Math.abs(x - 5),
+                      Math.abs(y - 5),
+                      10, 10,
+                      0, 0,
+                      200, 200);
+  };
+
+  canvas.addEventListener('mousemove', zoom);
+}
+ +

{{ EmbedLiveSample('Zoom_example', 620, 490) }}

+ +

Сохранение изображений

+ +

{{Domxref ("HTMLCanvasElement")}} предоставляет метод toDataURL(), который полезен при сохранении изображений. Он возвращает data URI, содержащий представление изображения в формате, заданном параметром type (по умолчанию используется в PNG ). Возвращаемое изображение имеет разрешение 96 точек на дюйм.

+ +
+
Примечание: 
+
Имейте в виду, что если холст содержит пиксели, полученные из другого {{Glossary ("origin")}} без использования CORS, холст будет испорчен, и его содержимое больше не будет считываться и сохраняться. Смотрите {{SectionOnPage ("/en-US/docs/Web/HTML/CORS_enabled_image", "Безопасность и испорченные холсты")}}
+
+
{{domxref("HTMLCanvasElement.toDataURL", "canvas.toDataURL('image/png')")}}
+
Настройки по умолчанию. Создает изображение в формате PNG.
+
{{domxref("HTMLCanvasElement.toDataURL", "canvas.toDataURL('image/jpeg', quality)")}}
+
Создает изображение в формате JPG. Дополнительно вы можете задать параметр "качество" (quality) в диапазоне от 0 до 1, причем единица задает лучшее качество и 0 -  почти не распознаваемый, но небольшой по размеру файл.
+
+ +

После того как вы создали URI данные из своего холста, вы можете использовать его как источник любого {{HTMLElement ("image")}} или поместить его в гиперссылку с download attribute, чтобы сохранить его на диске, например.

+ +

Вы также можете создать {{domxref ("Blob")}} из холста.

+ +
+
{{domxref("HTMLCanvasElement.toBlob", "canvas.toBlob(callback, type, encoderOptions)")}}
+
Создает объект Blob, представляющий изображение, содержащееся в холсте.
+
+ +

Смотрите также

+ + + +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Advanced_animations", "Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility")}}

diff --git a/files/ru/web/api/canvas_api/tutorial/transformations/index.html b/files/ru/web/api/canvas_api/tutorial/transformations/index.html new file mode 100644 index 0000000000..0f871d6909 --- /dev/null +++ b/files/ru/web/api/canvas_api/tutorial/transformations/index.html @@ -0,0 +1,275 @@ +--- +title: Transformations +slug: Web/API/Canvas_API/Tutorial/Transformations +translation_of: Web/API/Canvas_API/Tutorial/Transformations +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Using_images", "Web/API/Canvas_API/Tutorial/Compositing")}}
+ +
Ранее в этом уроке мы узнали о сетке холста и координатном пространстве . До сих пор мы использовали только сетку по умолчанию и изменили размер всего холста для наших нужд. При преобразованиях существуют более мощные способы изменения исходных координат в различные положение, поворот сетки и даже масштабирование.
+ +

Сохранение и восстановление состояния

+ +

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

+ +
+
{{domxref("CanvasRenderingContext2D.save", "save()")}}
+
Сохраняет все состояние холста.
+
{{domxref("CanvasRenderingContext2D.restore", "restore()")}}
+
Восстанавливает последнее сохраненное состояние холста.
+
+ +

Состояние холста сохраняется в стеке. Каждый раз, когда вызывается метод save(), текущее состояние отрисовки записывается в стек. Состояние отрисовки содержит:

+ + + +

Вы можете вызывать метод save() столько раз, сколько захотите. В то же время, при вызове метода restore() последнее сохранённое состояние будет считано из стека, и все сохранённые настройки будут восстановлены.

+ +

Пример сохранения и восстановления состояния холста

+ +

Здесь показано, как функционирует сохранение в стек состояния отрисовки на примере последовательной отрисовки набора прямоугольников.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  ctx.fillRect(0, 0, 150, 150);   // рисуем прямоугольник с настройками по умолчанию
+  ctx.save();                     // сохраняем состояние
+
+  ctx.fillStyle = '#09F';         // вносим изменения в настройки
+  ctx.fillRect(15, 15, 120, 120); // рисуем прямоугольник с новыми настройками
+  ctx.save();                     // сохраняем состояние
+
+  ctx.fillStyle = '#FFF';         // вносим изменения в настройки
+  ctx.globalAlpha = 0.5;
+  ctx.fillRect(30, 30, 90, 90);   // рисуем прямоугольник с новыми настройками
+
+  ctx.restore();                  // возвращаемся к предыдущим настройкам
+  ctx.fillRect(45, 45, 60, 60);   // рисуем прямоугольник с восстановленными настройками
+
+  ctx.restore();                  // возвращаемся к начальным настройкам
+  ctx.fillRect(60, 60, 30, 30);   // рисуем прямоугольник с изначальными настройками
+}
+ + + +

Сначала рисуется большой прямоугольник с настройками по умолчанию. Затем мы сохраняем состояние холста, после чего изменяем цвет заливки. Затем рисуем второй синий прямоугольник меньшего размера и опять сохраняем состояние. Снова изменяем какие-то настройки и рисуем третий полупрозрачный белый прямоугольник.

+ +

До сих пор наши действия ничем не отличались от тех, что мы делали в предыдущем разделе. Однако, как только мы сделали первый вызов restore(), последнее сохранённое состояние отрисовки было восстановлено из стека, вернув все сохранённые настройки. Если бы мы не сохранили предыдущее состояние, используя save(), нам бы пришлось менять цвет заливки и настройки прозрачности вручную для возврата к предыдущему состоянию. Для каких-нибудь двух простых свойств это, может быть, сделать не так сложно. Но если таких своиств гораздо больше, это чревато очень быстрым разрастанием кода.

+ +

Когда второй вызов restore() сделан, изначальное состояние (то самое, которое было сделано перед первым вызовом save) восстанавливается и последний нарисованный прямоугольник внось становится чёрным.

+ +

{{EmbedLiveSample("A_save_and_restore_canvas_state_example", "180", "180", "https://mdn.mozillademos.org/files/249/Canvas_savestate.png")}}

+ +

Трансляция (смещение)

+ +

Первый метод для трасформирования холста translate(). Он используется для перемещения холста в любую точку нашей сетки.

+ +
+
{{domxref("CanvasRenderingContext2D.translate", "translate(x, y)")}}
+
Перемещение холста на сетке. x и y - смещение по горизонтали и вертикали соответственно.
+
+ +

Неплохая идея - сохранять canvas state перед использованием любых трансформаций. Обычно удобнее использовать метод restore, чем выполнять обратные преобразования, чтобы вернуться к начальному состоянию. Кроме того, если вы выполняете преобразования внутри цикла не используя save и restore, вы рискуете потерять часть рисунка, потому что он был нарисован за пределами края холста.

+ +

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

+ +

Этот пример демонстрирует некоторые преимущества при использовании смещения холста. Без использования метода translate() все прямоугольники будут отрисованы в одинаковой позиции (0,0). Метод translate() дает возможность размещения прямоугольника в любой позиции без изменения параметров функции fillRect(). Это может дать некоторое упрощение для понимания и использования.

+ +

Внутри функции draw() мы вызываем fillRect() девять раз, используя два цикла for. Каждый раз мы сохраняем состояние холста, смещаем его, рисуем прямоугольник, а затем восстанавливаем исходное состояние. Заметьте, что fillRect() всегда использует одни и те же параметры, а изменение позиции фигуры осуществляется с помощью translate().

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  for (var i = 0; i < 3; i++) {
+    for (var j = 0; j < 3; j++) {
+      ctx.save();
+      ctx.fillStyle = 'rgb(' + (51 * i) + ', ' + (255 - 51 * i) + ', 255)';
+      ctx.translate(10 + j * 50, 10 + i * 50);
+      ctx.fillRect(0, 0, 25, 25);
+      ctx.restore();
+    }
+  }
+}
+
+ + + +

{{EmbedLiveSample("A_translate_example", "160", "160", "https://mdn.mozillademos.org/files/9857/translate.png")}}

+ +

Поворот

+ +

Второй метод трансформации rotate(). Он используется для поворота нашего холста.

+ +
+
{{domxref("CanvasRenderingContext2D.rotate", "rotate(angle)")}}
+
Поворачивает наш холст по часовой стрелке вокруг начальной точки на угол anglе в радианах.
+
+ +

Центр поворота - всегда начало координат. Для изменения координат центра мы должны сместить холст, используя метод translate().

+ +

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

+ +

В этом примере мы сначала используем rotate() для поворота прямоугольника относительно начала координат, а затем, используя translate() совместно с rotate() поворачиваем прямоугольник относительно его центра.

+ +
+

Памятка: Углы измеряются в радианах, а не в градусах. Для преобразования единиц используйте следующую формулу: radians = (Math.PI/180)*degrees.

+
+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // left rectangles, rotate from canvas origin
+  ctx.save();
+  // blue rect
+  ctx.fillStyle = '#0095DD';
+  ctx.fillRect(30, 30, 100, 100);
+  ctx.rotate((Math.PI / 180) * 25);
+  // grey rect
+  ctx.fillStyle = '#4D4E53';
+  ctx.fillRect(30, 30, 100, 100);
+  ctx.restore();
+
+  // right rectangles, rotate from rectangle center
+  // draw blue rect
+  ctx.fillStyle = '#0095DD';
+  ctx.fillRect(150, 30, 100, 100);
+
+  ctx.translate(200, 80); // translate to rectangle center
+                          // x = x + 0.5 * width
+                          // y = y + 0.5 * height
+  ctx.rotate((Math.PI / 180) * 25); // rotate
+  ctx.translate(-200, -80); // translate back
+
+  // draw grey rect
+  ctx.fillStyle = '#4D4E53';
+  ctx.fillRect(150, 30, 100, 100);
+}
+
+ +

Для поворота прямоугольника относительно его центра мы сначала смещаем начало координат, выполняем поворот, а затем выполняем обратное смещение к точке 0,0, и наконец рисуем прямоугольник.

+ + + +

{{EmbedLiveSample("A_rotate_example", "310", "210", "https://mdn.mozillademos.org/files/9859/rotate.png")}}

+ +

Масштабирование

+ +

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

+ +
+
{{domxref("CanvasRenderingContext2D.scale", "scale(x, y)")}}
+
Масштабирует координатную сетку холста по горизонтали и вертикали. Оба параметра - вещественные числа. Значения меньше 1.0 уменьшают, а больше 1.0 увеличивают масштаб сетки. Значение 1.0 не изменяет его.
+
+ +

Используя отрицательные значения вы можете зеркально отразить направление осей. Например, используя translate(0,canvas.height); scale(1,-1); вы получите хорошо известную декартову систему координат с началом в верхнем левом углу.

+ +

По умолчанию единица координатной сетки точно соответствует одному пикселю. Если же вы, например, зададите масштабный коэффициент 0.5, то единица сетки будет равна половине пикселя, и нарисованная фигура будет иметь размер в два раза меньше оригинала. Наоборот, если задать масштабный коэффициент 2.0, единица сетки будет соответствовать двум пикселям, а нарисованная фигура станет в два раза больше.

+ +

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

+ +

В этом примере мы нарисуем прямоугольники, используя разные масштабные коэффициенты.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // рисуем масштабированный прямоугольник.
+  ctx.save();
+  ctx.scale(10, 3);
+  ctx.fillRect(1, 10, 10, 10);
+  ctx.restore();
+
+  // размещаем текст, отраженный по горизонтали
+  ctx.scale(-1, 1);
+  ctx.font = '48px serif';
+  ctx.fillText('MDN', -135, 120);
+}
+
+
+ + + +

{{EmbedLiveSample("A_scale_example", "160", "160", "https://mdn.mozillademos.org/files/9861/scale.png")}}

+ +

Матричное преобразование

+ +

В заключении рассмотрим метод, который вызывает изменения в соответствии с матрицей преобразования.

+ +
+
{{domxref("CanvasRenderingContext2D.transform", "transform(a, b, c, d, e, f)")}}
+
Накладывает матрицу преобразования, заданную параметрами, на текущую матрицу. Матрица преобразования задается следующим образом: [acebdf001]\left[ \begin{array}{ccc} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{array} \right]
+
+ +
+
If any of the arguments are Infinity the transformation matrix must be marked as infinite instead of the method throwing an exception.
+
+ +

Параметры функции:

+ +
+
a (m11)
+
Horizontal scaling.
+
b (m12)
+
Horizontal skewing.
+
c (m21)
+
Vertical skewing.
+
d (m22)
+
Vertical scaling.
+
e (dx)
+
Horizontal moving.
+
f (dy)
+
Vertical moving.
+
{{domxref("CanvasRenderingContext2D.setTransform", "setTransform(a, b, c, d, e, f)")}}
+
Сбрасывает текущую матрицу преобразования, а затем вызываетtransform() в соответствии с аргументами.
+
{{domxref("CanvasRenderingContext2D.resetTransform", "resetTransform()")}}
+
Сбрасывает текущую матрицу преобразования к значению по умолчанию. Аналогично вызову ctx.setTransform(1, 0, 0, 1, 0, 0);
+
+ +

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

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  var sin = Math.sin(Math.PI / 6);
+  var cos = Math.cos(Math.PI / 6);
+  ctx.translate(100, 100);
+  var c = 0;
+  for (var i = 0; i <= 12; i++) {
+    c = Math.floor(255 / 12 * i);
+    ctx.fillStyle = 'rgb(' + c + ', ' + c + ', ' + c + ')';
+    ctx.fillRect(0, 0, 100, 10);
+    ctx.transform(cos, sin, -sin, cos, 0, 0);
+  }
+
+  ctx.setTransform(-1, 0, 0, 1, 100, 100);
+  ctx.fillStyle = 'rgba(255, 128, 255, 0.5)';
+  ctx.fillRect(0, 50, 100, 100);
+}
+
+ + + +

{{EmbedLiveSample("Example_for_transform_and_setTransform", "230", "280", "https://mdn.mozillademos.org/files/255/Canvas_transform.png")}}

+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Using_images", "Web/API/Canvas_API/Tutorial/Compositing")}}

diff --git "a/files/ru/web/api/canvas_api/tutorial/\320\270\321\201\320\277\320\276\320\273\321\214\320\267\320\276\320\262\320\260\320\275\320\270\320\265_\320\270\320\267\320\276\320\261\321\200\320\260\320\266\320\265\320\275\320\270\320\271/index.html" "b/files/ru/web/api/canvas_api/tutorial/\320\270\321\201\320\277\320\276\320\273\321\214\320\267\320\276\320\262\320\260\320\275\320\270\320\265_\320\270\320\267\320\276\320\261\321\200\320\260\320\266\320\265\320\275\320\270\320\271/index.html" new file mode 100644 index 0000000000..3ce4b8384e --- /dev/null +++ "b/files/ru/web/api/canvas_api/tutorial/\320\270\321\201\320\277\320\276\320\273\321\214\320\267\320\276\320\262\320\260\320\275\320\270\320\265_\320\270\320\267\320\276\320\261\321\200\320\260\320\266\320\265\320\275\320\270\320\271/index.html" @@ -0,0 +1,333 @@ +--- +title: Использование изображений +slug: Web/API/Canvas_API/Tutorial/Использование_изображений +tags: + - Графика +translation_of: Web/API/Canvas_API/Tutorial/Using_images +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_text", "Web/API/Canvas_API/Tutorial/Трансформации")}}
+ +
+

До сих пор мы создавали наши собственные фигуры и применяли стили к ним. Одна из самых впечатляющих функций {{HTMLElement("canvas")}} это возможность использования изображений. Они могут быть использованы для динамического композитинга фото или как фоны графиков, для спрайтов в играх, и так далее. Внешние изображения могут быть использованы в любых поддерживаемых браузером форматах, таких как PNG, GIF, или JPEG. Вы можете даже использовать изображение, произведенное другими canvas элементами на той же странице как источник!

+
+ +

Импортирование изображений в canvas в основном состоит из 2 этапов:

+ +
    +
  1. Дав ссылку на {{domxref("HTMLImageElement")}} объект или для другого canvas элемента как источник. Также можно использовать изображение дав ссылку на URL.
  2. +
  3. Для рисования изображения на canvas используется функция drawImage().
  4. +
+ +

Давайте посмотрим как это сделать.

+ +

Использование изображений для рисования

+ +

Canvas API может использовать все перечисленные далее типы данных как источник изображения:

+ +
+
{{domxref("HTMLImageElement")}}
+
Эти изображения созданы, используя конструктор Image(), также как все{{HTMLElement("img")}} элементы.
+
{{domxref("HTMLVideoElement")}}
+
Используя HTML {{HTMLElement("video")}} элемент как источник изображения захватывает текущий кадр из видео и использует его как изображение.
+
{{domxref("HTMLCanvasElement")}}
+
Вы можете использовать другой {{HTMLElement("canvas")}} элемент как источник изображения.
+
+ +

Эти источники совместно именуемые по типу {{domxref("CanvasImageSource")}}.

+ +

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

+ +

Использование изображений из той же страницы

+ +

Мы можем получить ссылку на изображение, на той же странице, на canvas с используя  один из способов: 

+ + + +

Использование изображений из других доменов

+ +

Использование {{htmlattrxref("crossorigin", "img")}} атрибута {{HTMLElement("img")}} элемент (отображается  {{domxref("HTMLImageElement.crossOrigin")}} свойства), вы можете запросить разрешение на загрузку другого домена для использования в drawImage(). Если хостинг домен разрешает доступ к междоменному изображению, то изображение может быть использовано в вашем canvas без  without tainting it;иначе он может испортить ваш canvas.

+ +

Использование других canvas элементов

+ +

Как и с обычными изображениями, мы можем получить доступ к другим canvas элементам используя либо {{domxref("document.getElementsByTagName()")}} либо {{domxref("document.getElementById()")}} метод. Проверьте, что в canvas источнике уже что-то нарисовано, прежде чем использовать его в целевом изображении canvas.

+ +

Одним из удобных способов было бы использование второго элемента canvas  в качестве миниатюры другого большего изображения canvas.

+ +

Создание изображений с нуля

+ +

Другой способ это создать новые {{domxref("HTMLImageElement")}} объекты в нашем скрипте.  Чтобы это сделать, вы можете использовать удобный Image() конструктор:

+ +
var img = new Image();   // Создает новый элемент изображения
+img.src = 'myImage.png'; // Устанавливает путь
+
+ +

Когда этот скрипт выполнится, изображение начнет загружаться.

+ +

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

+ +
var img = new Image();   // Создает новое изображение
+img.addEventListener("load", function() {
+  // здесь выполняет drawImage функцию
+}, false);
+img.src = 'myImage.png'; // Устанавливает источник файла
+
+ +

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

+ +

Вложение изображения с помощью данных: URL

+ +

Другой возможный способ включить изображение это через data: url. Data URLs позволяет вам полностью определить изображение как Base64 кодированную строку символов прямо в ваш код.

+ +
var img = new Image();   // Создает новый элемент img
+img.src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
+
+ +

Одним из преимуществ data URLs  это то что полученное изображение доступно сразу без других запросов туда-обратно на сервер. Другое потенциальное преимущество в том, что также можно инкапсулировать всё в одном файле все ваши CSS, JavaScript, HTML, и изображения, что делает его более портативным в других местах.

+ +

Некоторые недостатки этого метода в том что ваше изображение не кешировано, и для изображений с большим размером кодированние url может стать очень долгим процессом.

+ +

Использование кадров из видео

+ +

Вы также можете использовать кадры из видео представленных {{HTMLElement("video")}} элементом (даже если видео не видно). Например, если у вас есть  {{HTMLElement("video")}} элемент с  ID "myvideo", вы можете сделать:

+ +
function getMyVideo() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    return document.getElementById('myvideo');
+  }
+}
+
+ +

Эта функция вернет {{domxref("HTMLVideoElement")}} объект для этого видео, который, как мы упоминали ранее, является одним из объектов, который можно использовать как CanvasImageSource.

+ +

Рисование изображений

+ +

Как только мы получили ссылку на источник объекта изображения, мы можем использовать метод drawImage() для включения его в  canvas. Как мы увидим далее, метод drawImage() перегружен и у него есть несколько вариантов. В базовом варианте он выглядит как:

+ +
+
{{domxref("CanvasRenderingContext2D.drawImage", "drawImage(image, x, y)")}}
+
Рисует  изображение, указанное в CanvasImageSource в координатах  (x, y).
+
+ +
+

SVG изображения должны указывать ширину и высоту корневого  <svg> элемента.

+
+ +

Пример: Простой линейный график

+ +

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

+ + + +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var img = new Image();
+  img.onload = function(){
+    ctx.drawImage(img,0,0);
+    ctx.beginPath();
+    ctx.moveTo(30,96);
+    ctx.lineTo(70,66);
+    ctx.lineTo(103,76);
+    ctx.lineTo(170,15);
+    ctx.stroke();
+  };
+  img.src = 'https://mdn.mozillademos.org/files/5395/backdrop.png';
+}
+ +

Получившийся график выглядит так:

+ +

{{EmbedLiveSample("Example_A_simple_line_graph", 220, 160, "https://mdn.mozillademos.org/files/206/Canvas_backdrop.png")}}

+ +

Изменение размеров

+ +

Второй вариант метода drawImage() добавляет два новых параметра и позволяет разместить изображение в  canvas с измененными размерами.

+ +
+
{{domxref("CanvasRenderingContext2D.drawImage", "drawImage(image, x, y, width, height)")}}
+
Это добавляет параметр ширины и высоты, которые указывают до какого размера нужно изменить изображение при рисовании его в  canvas.
+
+ +

Пример: Тайлинг изображения

+ +

В этом примере, мы будем использовать изображение в качестве обоев и повторим его в canvas несколько раз. Это может быть сделано просто через цикл, располагая измененные изображения на разных позициях. В коде внизу, первый цикл for проходит по рядам. Второй цикл for проходит по колонкам. Изображение уменьшено на треть от реального размера, которое было  50x38 пикселей.

+ +
+

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

+
+ + + +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var img = new Image();
+  img.onload = function(){
+    for (var i=0;i<4;i++){
+      for (var j=0;j<3;j++){
+        ctx.drawImage(img,j*50,i*38,50,38);
+      }
+    }
+  };
+  img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
+}
+ +

Получившийся рисунок canvas выглядит так:

+ +

{{EmbedLiveSample("Example_Tiling_an_image", 160, 160, "https://mdn.mozillademos.org/files/251/Canvas_scale_image.png")}}

+ +

Нарезка

+ +

У третьего и последнего варианта метода drawImage() в дополнении к источнику изображения есть еще восемь параметров . Он позволяет нам вырезать кусок из изображения, затем изменить его размер и нарисовать его в canvas.

+ +
+
{{domxref("CanvasRenderingContext2D.drawImage", "drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)")}}
+
В данном изображении, эта функция берет фрагмент из изображения, в виде прямоугольника, левый верхний угол которого -  (sx, sy), ширина и высота -  sWidth и sHeight и рисует  в  canvas, располагая его в точке  (dx, dy) и изменяя его размер на указанные величины в  dWidth и dHeight.
+
+ +

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

+ +

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

+ +

Пример: Обрамление изображения

+ +

В этом примере, мы будем использовать того же носорога, что и в предыдущем примере, но мы отрежем его голову и включим ее в рамку. Изображение рамки это 24-х битный PNG, который включает падающую тень. Так как в 24-х битные PNG изображения включается полный 8-ми битный альфа-канал, в отличие от GIF и 8-битных PNG изображений, он может быть помещен в любой фон, без беспокойства о матовом цвете. 

+ +
<html>
+ <body onload="draw();">
+   <canvas id="canvas" width="150" height="150"></canvas>
+   <div style="display:none;">
+     <img id="source" src="https://mdn.mozillademos.org/files/5397/rhino.jpg" width="300" height="227">
+     <img id="frame" src="https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png" width="132" height="150">
+   </div>
+ </body>
+</html>
+
+ +
function draw() {
+  var canvas = document.getElementById('canvas');
+  var ctx = canvas.getContext('2d');
+
+  // Рисуем фрагмент
+  ctx.drawImage(document.getElementById('source'),
+                33, 71, 104, 124, 21, 20, 87, 104);
+
+  // Рисуем рамку
+  ctx.drawImage(document.getElementById('frame'),0,0);
+}
+ +

В этот раз мы применили другой способ загрузки изображения. Вместо загрузки методом создания новых {{domxref("HTMLImageElement")}} объектов, мы включили их как  {{HTMLElement("img")}} тэги прямо в наш HTML файл и из них выбрали изображения. Изображения скрыты с помощью  CSS свойства {{cssxref("display")}}, установленного в "none" для этих изображений.

+ +

{{EmbedLiveSample("Example_Framing_an_image", 160, 160, "https://mdn.mozillademos.org/files/226/Canvas_drawimage2.jpg")}}

+ +

Скрипт, сам по себе, очень простой. Каждому {{HTMLElement("img")}} присвоен атрибут ID, который  делает удобным их выбор с использованием {{domxref("document.getElementById()")}}. Потом мы просто используем функцию  drawImage(), чтобы из первого изображения вырезать фрагмент носорога и вставить его в canvas, затем рисуем рамку сверху, используя второй вызов функции drawImage().

+ +

Пример галереи искусства

+ +

В последнем примере этой главы, мы построим небольшую галлерею искусств. Галерея состоит из таблицы, включающей несколько изображений. Когда страница загрузится,  {{HTMLElement("canvas")}}  элемент вставится в каждое изображение, а вокруг будет нарисована рамка. 

+ +

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

+ +

Код ниже должен говорить сам за себя. Мы проходим циклом через {{domxref("document.images")}} контейнер и соответственно добавляем новые элементы  canvas. Возможно следует упомянуть для тех, кто не слишком хорошо знаком с DOM, что для этого используется {{domxref("Node.insertBefore")}} метод. insertBefore() это метод родительского узла (ячейки таблицы) элемента (изображения) перед которым мы хотим вставить наш новый узел  (элемент canvas).

+ +
<html>
+ <body onload="draw();">
+     <table>
+      <tr>
+        <td><img src="https://mdn.mozillademos.org/files/5399/gallery_1.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5401/gallery_2.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5403/gallery_3.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5405/gallery_4.jpg"></td>
+      </tr>
+      <tr>
+        <td><img src="https://mdn.mozillademos.org/files/5407/gallery_5.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5409/gallery_6.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5411/gallery_7.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5413/gallery_8.jpg"></td>
+      </tr>
+     </table>
+     <img id="frame" src="https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png" width="132" height="150">
+ </body>
+</html>
+
+ +

И сюда какую-нибудь CSS для украшения:

+ +
body {
+  background: 0 -100px repeat-x url(https://mdn.mozillademos.org/files/5415/bg_gallery.png) #4F191A;
+  margin: 10px;
+}
+
+img {
+  display: none;
+}
+
+table {
+  margin: 0 auto;
+}
+
+td {
+  padding: 15px;
+}
+
+ +

Связывая все вместе  JavaScript рисует наши изображения в рамках:

+ +
function draw() {
+
+  // Цикл по всем изображениям
+  for (var i=0;i<document.images.length;i++){
+
+    // Не добавляет canvas для изображения рамки
+    if (document.images[i].getAttribute('id')!='frame'){
+
+      // Создает элемент canvas
+      var canvas = document.createElement('canvas');
+      canvas.setAttribute('width',132);
+      canvas.setAttribute('height',150);
+
+      // Вставляет перед изображением
+      document.images[i].parentNode.insertBefore(canvas,document.images[i]);
+
+      var ctx = canvas.getContext('2d');
+
+      // Рисует изображение в canvas
+      ctx.drawImage(document.images[i],15,20);
+
+      // Добавляет рамку
+      ctx.drawImage(document.getElementById('frame'),0,0);
+    }
+  }
+}
+ +

{{EmbedLiveSample("Art_gallery_example", 725, 400)}}

+ +

Контроль изменений размеров изображения

+ +

Как было отмечено ранее, изменение размеров изображений может привести к размытости или к шуму в процессе преобразования. Вы можете использовать контекст рисования {{domxref("CanvasRenderingContext2D.imageSmoothingEnabled", "imageSmoothingEnabled")}} свойства, чтобы контролировать использование сглаживающего алгоритма, когда изменяющиеся изображения в вашем контексте. Обычно это свойство установлено в  true, означая, что изображения будут сглажены во время изменения размеров. Вы можете отключить это свойство так:

+ +
ctx.mozImageSmoothingEnabled = false;
+ctx.webkitImageSmoothingEnabled = false;
+ctx.msImageSmoothingEnabled = false;
+ctx.imageSmoothingEnabled = false;
+
+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_text", "Web/API/Canvas_API/Tutorial/Transformations")}}

diff --git "a/files/ru/web/api/canvas_api/tutorial/\320\272\320\276\320\274\320\277\320\276\320\267\320\270\321\206\320\270\320\270/index.html" "b/files/ru/web/api/canvas_api/tutorial/\320\272\320\276\320\274\320\277\320\276\320\267\320\270\321\206\320\270\320\270/index.html" new file mode 100644 index 0000000000..264cc7e544 --- /dev/null +++ "b/files/ru/web/api/canvas_api/tutorial/\320\272\320\276\320\274\320\277\320\276\320\267\320\270\321\206\320\270\320\270/index.html" @@ -0,0 +1,108 @@ +--- +title: Композиция и обрезка +slug: Web/API/Canvas_API/Tutorial/Композиции +tags: + - канвас +translation_of: Web/API/Canvas_API/Tutorial/Compositing +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Transformations", "Web/API/Canvas_API/Tutorial/Basic_animations")}}
+ +
+

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

+
+ +

globalCompositeOperation

+ +

Мы можем не только рисовать новые фигуры за существующие формы, но мы также можем использовать его, чтобы замаскировать определенные участки, очистить разделы от холста (не ограничивается прямоугольниками, как{{domxref("CanvasRenderingContext2D.clearRect", "clearRect()")}} method does) и другое.

+ +
+
{{domxref("CanvasRenderingContext2D.globalCompositeOperation", "globalCompositeOperation = type")}}
+
Это задает Тип операции композиции для применения при разработке новых форм, где Тип является строкой, идентифицирующей, какие из двенадцати операций композитинг в использовании.
+
+ +

См.  примеры компоновки кода из следующих примеров.

+ +

{{EmbedLiveSample("Compositing_example", 750, 6750, "" ,"Web/API/Canvas_API/Tutorial/Compositing/Example")}}

+ +

Обрезка контуров

+ +

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

+ +

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

+ +

В главе о рисовании форм, я назвал только stroke() и fill() методы, но есть третий способ можно использовать с контурами, так называемый clip().

+ +
+
{{domxref("CanvasRenderingContext2D.clip", "clip()")}}
+
Преобразует текущий выстраиваемый контур в отсечённый контур.
+
+ +

Используйте clip() вместо closePath() для закрытия контура и его преобразования в отсечённый контур вместо создания заполняющего  или обрамляющего контура.

+ +

По умолчанию элемент {{HTMLElement("canvas")}} использует отсечённый контур, который в точности совпадает по размеру с размером самого холста. Это означает, что никакого отсечения попросту не произойдёт.

+ +

Пример обрезки

+ +

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

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.fillRect(0, 0, 150, 150);
+  ctx.translate(75, 75);
+
+  // Create a circular clipping path
+  ctx.beginPath();
+  ctx.arc(0, 0, 60, 0, Math.PI * 2, true);
+  ctx.clip();
+
+  // draw background
+  var lingrad = ctx.createLinearGradient(0, -75, 0, 75);
+  lingrad.addColorStop(0, '#232256');
+  lingrad.addColorStop(1, '#143778');
+
+  ctx.fillStyle = lingrad;
+  ctx.fillRect(-75, -75, 150, 150);
+
+  // draw stars
+  for (var j = 1; j < 50; j++) {
+    ctx.save();
+    ctx.fillStyle = '#fff';
+    ctx.translate(75 - Math.floor(Math.random() * 150),
+                  75 - Math.floor(Math.random() * 150));
+    drawStar(ctx, Math.floor(Math.random() * 4) + 2);
+    ctx.restore();
+  }
+
+}
+
+function drawStar(ctx, r) {
+  ctx.save();
+  ctx.beginPath();
+  ctx.moveTo(r, 0);
+  for (var i = 0; i < 9; i++) {
+    ctx.rotate(Math.PI / 5);
+    if (i % 2 === 0) {
+      ctx.lineTo((r / 0.525731) * 0.200811, 0);
+    } else {
+      ctx.lineTo(r, 0);
+    }
+  }
+  ctx.closePath();
+  ctx.fill();
+  ctx.restore();
+}
+
+ + + +

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

+ +

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

+ +

{{EmbedLiveSample("A_clip_example", "180", "180", "https://mdn.mozillademos.org/files/208/Canvas_clip.png")}}

+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Transformations", "Web/API/Canvas_API/Tutorial/Basic_animations")}}

diff --git "a/files/ru/web/api/canvas_api/tutorial/\320\276\321\201\320\275\320\276\320\262\321\213_\320\260\320\275\320\270\320\274\320\260\321\206\320\270\320\270/index.html" "b/files/ru/web/api/canvas_api/tutorial/\320\276\321\201\320\275\320\276\320\262\321\213_\320\260\320\275\320\270\320\274\320\260\321\206\320\270\320\270/index.html" new file mode 100644 index 0000000000..a47b8b734e --- /dev/null +++ "b/files/ru/web/api/canvas_api/tutorial/\320\276\321\201\320\275\320\276\320\262\321\213_\320\260\320\275\320\270\320\274\320\260\321\206\320\270\320\270/index.html" @@ -0,0 +1,308 @@ +--- +title: Простые анимации +slug: Web/API/Canvas_API/Tutorial/Основы_анимации +tags: + - HTML + - HTML5 + - Графика + - Обучение + - Средний уровень + - Холст +translation_of: Web/API/Canvas_API/Tutorial/Basic_animations +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Compositing", "Web/API/Canvas_API/Tutorial/Advanced_animations")}}
+ +
+

Поскольку для управления элементами {{HTMLElement ("canvas")}} используется JavaScript, не составляет труда сделать (интерактивные) анимации. В этой главе мы рассмотрим, как делаются некоторые базовые анимации.

+
+ +

Вероятно, самым большим ограничением является то, что когда фигура нарисована, её уже нельзя двигать. Чтобы изобразить движение нам нужно перерисовать фигуру и всё, что было нарисовано до неё. Перерисовка сложных кадров занимает много времени, и производительность сильно зависит от скорости компьютера, на котором она выполняется.

+ +

Основные шаги анимации

+ +

Ниже перечислены необходимые шаги для того, чтобы нарисовать кадр:

+ +
    +
  1. Очистить canvas
    + Если фигура, которую вы собираетесь нарисовать, не занимает всю площадь canvas (как фон, например), то всё что было нарисовано ранее необходимо стереть. Проще всего это сделать при помощи метода {{domxref("CanvasRenderingContext2D.clearRect", "clearRect()")}}.
  2. +
  3. Сохранить изначальное состояние canvas
    + Если вы изменяете любые настройки (такие как стили, трансформации и т.п.), которые затрагивают состояние canvas и вы хотите убедиться, что оригинальное состояние используется каждый раз, когда был отрисован кадр, то вам следует сохранить это оригинальное состояние.
  4. +
  5. Нарисовать анимированные фигуры
    + Шаг на котором вы собственно отрисовываете кадр.
  6. +
  7. Восстановить состояние canvas
    + Если вы сохраняли состояние, восстановите его, прежде чем отрисовывать новый кадр.
  8. +
+ +

Управление анимацией

+ +

Фигуры отрисовываются на canvas либо напрямую — при помощи методов canvas, либо с помощью сторонних функций. В нормальной ситуации результат станет виден на canvas после окончания выполнения скрипта. К примеру, цикл for использовать для анимации нельзя. 

+ +

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

+ +

Запланированные обновления

+ +

Первый — это функции {{domxref("window.setInterval()")}}, {{domxref("window.setTimeout()")}}, и {{domxref("window.requestAnimationFrame()")}}, которые могут быть использованы для вызова некоторой функции, через заданный промежуток времени.

+ +
+
{{domxref("WindowTimers.setInterval", "setInterval(function, delay)")}}
+
Начинает периодически исполнять функцию function каждые delay миллисекунд.
+
{{domxref("WindowTimers.setTimeout", "setTimeout(function, delay)")}}
+
Запускает выполнение указанной функции function через delay миллисекунд.
+
{{domxref("Window.requestAnimationFrame()", "requestAnimationFrame(callback)")}}
+
Сообщает браузеру, что вы хотите выполнить анимацию, и запрашивает, чтобы браузер вызвал указанную функцию callback для обновления анимации перед следующей перерисовкой.
+
+ +

Если вы не планируете никакого взаимодействия с пользователем, вы можете использовать функцию setInterval() , которая многократно выполняет, предоставленный ей код. Если же вы планиуете создать игру, в которой контроль анимации осуществляется мышью или клавиатурой, то необходимо использовать  setTimeout(). Установив {{domxref("EventListener")}}, вы можете перехватываете любые действия пользователя и запустить соответствующие функции анимации.

+ +
+

В примерах ниже мы будем использовать функцию {{domxref("window.requestAnimationFrame()")}} для контроля анимации. Функция requestAnimationFrame является более эффективной для создания анимации, так как новая итерация вызывается, когда система готова к отрисовке нового кадра. Количество вызовов в секунду примерно равно 60 и уменьшается, когда вкладка неактивна. Для более подробного изучения цикла анимации, особенно для игр, прочитайте статью Анатомия видеоигр В Зоне разработке игр.

+
+ +

Анимированная солнечная система

+ +

В этом примере анимируется небольшая модель солнечной системы.

+ +
var sun = new Image();
+var moon = new Image();
+var earth = new Image();
+function init(){
+  sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
+  moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
+  earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
+  window.requestAnimationFrame(draw);
+}
+
+function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  ctx.globalCompositeOperation = 'destination-over';
+  ctx.clearRect(0,0,300,300); // clear canvas
+
+  ctx.fillStyle = 'rgba(0,0,0,0.4)';
+  ctx.strokeStyle = 'rgba(0,153,255,0.4)';
+  ctx.save();
+  ctx.translate(150,150);
+
+  // Earth
+  var time = new Date();
+  ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );
+  ctx.translate(105,0);
+  ctx.fillRect(0,-12,50,24); // Shadow
+  ctx.drawImage(earth,-12,-12);
+
+  // Moon
+  ctx.save();
+  ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );
+  ctx.translate(0,28.5);
+  ctx.drawImage(moon,-3.5,-3.5);
+  ctx.restore();
+
+  ctx.restore();
+
+  ctx.beginPath();
+  ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit
+  ctx.stroke();
+
+  ctx.drawImage(sun,0,0,300,300);
+
+  window.requestAnimationFrame(draw);
+}
+
+init();
+
+ + + +

{{EmbedLiveSample("An_animated_solar_system", "310", "310", "https://mdn.mozillademos.org/files/202/Canvas_animation1.png")}}

+ +

Анимированные часы

+ +

В этом примере создаются анимированные часы, показывающие правильное время.

+ +
function clock(){
+  var now = new Date();
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.save();
+  ctx.clearRect(0,0,150,150);
+  ctx.translate(75,75);
+  ctx.scale(0.4,0.4);
+  ctx.rotate(-Math.PI/2);
+  ctx.strokeStyle = "black";
+  ctx.fillStyle = "white";
+  ctx.lineWidth = 8;
+  ctx.lineCap = "round";
+
+  // Hour marks
+  ctx.save();
+  for (var i=0;i<12;i++){
+    ctx.beginPath();
+    ctx.rotate(Math.PI/6);
+    ctx.moveTo(100,0);
+    ctx.lineTo(120,0);
+    ctx.stroke();
+  }
+  ctx.restore();
+
+  // Minute marks
+  ctx.save();
+  ctx.lineWidth = 5;
+  for (i=0;i<60;i++){
+    if (i%5!=0) {
+      ctx.beginPath();
+      ctx.moveTo(117,0);
+      ctx.lineTo(120,0);
+      ctx.stroke();
+    }
+    ctx.rotate(Math.PI/30);
+  }
+  ctx.restore();
+
+  var sec = now.getSeconds();
+  var min = now.getMinutes();
+  var hr  = now.getHours();
+  hr = hr>=12 ? hr-12 : hr;
+
+  ctx.fillStyle = "black";
+
+  // write Hours
+  ctx.save();
+  ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec )
+  ctx.lineWidth = 14;
+  ctx.beginPath();
+  ctx.moveTo(-20,0);
+  ctx.lineTo(80,0);
+  ctx.stroke();
+  ctx.restore();
+
+  // write Minutes
+  ctx.save();
+  ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec )
+  ctx.lineWidth = 10;
+  ctx.beginPath();
+  ctx.moveTo(-28,0);
+  ctx.lineTo(112,0);
+  ctx.stroke();
+  ctx.restore();
+
+  // Write seconds
+  ctx.save();
+  ctx.rotate(sec * Math.PI/30);
+  ctx.strokeStyle = "#D40000";
+  ctx.fillStyle = "#D40000";
+  ctx.lineWidth = 6;
+  ctx.beginPath();
+  ctx.moveTo(-30,0);
+  ctx.lineTo(83,0);
+  ctx.stroke();
+  ctx.beginPath();
+  ctx.arc(0,0,10,0,Math.PI*2,true);
+  ctx.fill();
+  ctx.beginPath();
+  ctx.arc(95,0,10,0,Math.PI*2,true);
+  ctx.stroke();
+  ctx.fillStyle = "rgba(0,0,0,0)";
+  ctx.arc(0,0,3,0,Math.PI*2,true);
+  ctx.fill();
+  ctx.restore();
+
+  ctx.beginPath();
+  ctx.lineWidth = 14;
+  ctx.strokeStyle = '#325FA2';
+  ctx.arc(0,0,142,0,Math.PI*2,true);
+  ctx.stroke();
+
+  ctx.restore();
+
+  window.requestAnimationFrame(clock);
+}
+
+window.requestAnimationFrame(clock);
+ + + +

{{EmbedLiveSample("An_animated_clock", "180", "180", "https://mdn.mozillademos.org/files/203/Canvas_animation2.png")}}

+ +

Зацикленная панорама

+ +

В этом примере панорама прокручивается слева направо. Мы используем фото национального парка Йосемити взятое из Википедии, но вы можете использовать любое изображение, большее элемента canvas.

+ +
var img = new Image();
+
+// User Variables - customize these to change the image being scrolled, its
+// direction, and the speed.
+
+img.src = 'https://mdn.mozillademos.org/files/4553/Capitan_Meadows,_Yosemite_National_Park.jpg';
+var CanvasXSize = 800;
+var CanvasYSize = 200;
+var speed = 30; //lower is faster
+var scale = 1.05;
+var y = -4.5; //vertical offset
+
+// Main program
+
+var dx = 0.75;
+var imgW;
+var imgH;
+var x = 0;
+var clearX;
+var clearY;
+var ctx;
+
+img.onload = function() {
+    imgW = img.width*scale;
+    imgH = img.height*scale;
+    if (imgW > CanvasXSize) { x = CanvasXSize-imgW; } // image larger than canvas
+    if (imgW > CanvasXSize) { clearX = imgW; } // image larger than canvas
+    else { clearX = CanvasXSize; }
+    if (imgH > CanvasYSize) { clearY = imgH; } // image larger than canvas
+    else { clearY = CanvasYSize; }
+    //Get Canvas Element
+    ctx = document.getElementById('canvas').getContext('2d');
+    //Set Refresh Rate
+    return setInterval(draw, speed);
+}
+
+function draw() {
+    //Clear Canvas
+    ctx.clearRect(0,0,clearX,clearY);
+    //If image is <= Canvas Size
+    if (imgW <= CanvasXSize) {
+        //reset, start from beginning
+        if (x > (CanvasXSize)) { x = 0; }
+        //draw aditional image
+        if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-CanvasXSize+1,y,imgW,imgH); }
+    }
+    //If image is > Canvas Size
+    else {
+        //reset, start from beginning
+        if (x > (CanvasXSize)) { x = CanvasXSize-imgW; }
+        //draw aditional image
+        if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-imgW+1,y,imgW,imgH); }
+    }
+    //draw image
+    ctx.drawImage(img,x,y,imgW,imgH);
+    //amount to move
+    x += dx;
+}
+
+ +

Заметьте, что ширина и высота должны совпадать  со значениями CanvasXZSize и CanvasYSize.

+ +
<canvas id="canvas" width="800" height="200"></canvas>
+ +

{{EmbedLiveSample("A_looping_panorama", "830", "230")}}

+ +

Другие примеры

+ +
+
A basic ray-caster
+
Хороший пример того, как сделать управляемую анимацию с клавиатуры.
+
Advanced animations
+
Мы рассмотрим некоторые продвинутые методы анимации и физику в следующей главе.
+
+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Compositing", "Web/API/Canvas_API/Tutorial/Advanced_animations")}}

diff --git "a/files/ru/web/api/canvas_api/tutorial/\320\277\321\200\320\270\320\274\320\265\320\275\320\265\320\275\320\270\320\265_\321\201\321\202\320\270\320\273\320\265\320\271_\320\270_\321\206\320\262\320\265\321\202\320\276\320\262/index.html" "b/files/ru/web/api/canvas_api/tutorial/\320\277\321\200\320\270\320\274\320\265\320\275\320\265\320\275\320\270\320\265_\321\201\321\202\320\270\320\273\320\265\320\271_\320\270_\321\206\320\262\320\265\321\202\320\276\320\262/index.html" new file mode 100644 index 0000000000..2c9eeaae78 --- /dev/null +++ "b/files/ru/web/api/canvas_api/tutorial/\320\277\321\200\320\270\320\274\320\265\320\275\320\265\320\275\320\270\320\265_\321\201\321\202\320\270\320\273\320\265\320\271_\320\270_\321\206\320\262\320\265\321\202\320\276\320\262/index.html" @@ -0,0 +1,726 @@ +--- +title: Применение стилей и цветов +slug: Web/API/Canvas_API/Tutorial/Применение_стилей_и_цветов +translation_of: Web/API/Canvas_API/Tutorial/Applying_styles_and_colors +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_shapes", "Web/API/Canvas_API/Tutorial/Drawing_text")}}
+ +
+

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

+
+ +

Цвета

+ +

До сих пор мы видели только методы рисования контекста. Если мы хотим применить цвета к фигуре, то есть два важных свойства, которые мы можем использовать: fillStyle и strokeStyle.

+ +
+
{{domxref("CanvasRenderingContext2D.fillStyle", "fillStyle = color")}}
+
Устанавливает стиль для фона фигур.
+
{{domxref("CanvasRenderingContext2D.strokeStyle", "strokeStyle = color")}}
+
Устанавливает стиль контура фигуры. 
+
+ +

color может быть цветом, (строка, представленная в CSS {{cssxref("<color>")}}), градиентом или паттерном. Градиенты и паттерны мы рассмотрим позже. По умолчанию цвет фона и контура  — черный (значение CSS цвета  #000000).

+ +
+

На заметку: Когда вы устанавливаете  значения strokeStyle и/или fillStyle, то новое значение становится стандартным для всех фигур, которые будут нарисованы с этого момента. Когда вам нужен другой цвет, вы должны перезаписать значение в fillStyle или в strokeStyle для каждой фигуры.

+
+ +

Чтобы строка color считалась валидной, она должна соответствовать CSS {{cssxref("<color>")}}. Далее приведены примеры того, как можно по-разному задать один и тот же цвет. 

+ +
// these all set the fillStyle to 'orange'
+
+ctx.fillStyle = "orange";
+ctx.fillStyle = "#FFA500";
+ctx.fillStyle = "rgb(255,165,0)";
+ctx.fillStyle = "rgba(255,165,0,1)";
+
+ +

Пример fillStyle

+ +

В этом примере мы опять воспользуемся двойным циклом, чтобы нарисовать сетку из прямоугольников, каждый из которых имеет свой цвет. Окончательное изображение должно иметь вид, как показано на скриншоте. Здесь не происходит ничего сверхъестественного. Мы используем две переменные i и j для генерации уникального RGB цвета для каждого квадрата и изменяем только красные и зеленые значения. Синий канал представляет собой фиксированное значение. Путем изменения каналов вы можете генерировать всю палитру. Увеличив количество шагов вы можете достигнуть такого вида палитры, какая используется в Photoshop.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  for (var i=0;i<6;i++){
+    for (var j=0;j<6;j++){
+      ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' +
+                       Math.floor(255-42.5*j) + ',0)';
+      ctx.fillRect(j*25,i*25,25,25);
+    }
+  }
+}
+ + + +

Результат выглядит так:

+ +

{{EmbedLiveSample("Пример_fillStyle", 160, 160, "https://mdn.mozillademos.org/files/5417/Canvas_fillstyle.png")}}

+ +

Пример strokeStyle

+ +

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

+ +
  function draw() {
+    var ctx = document.getElementById('canvas').getContext('2d');
+    for (var i=0;i<6;i++){
+      for (var j=0;j<6;j++){
+        ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' +
+                         Math.floor(255-42.5*j) + ')';
+        ctx.beginPath();
+        ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true);
+        ctx.stroke();
+      }
+    }
+  }
+
+ + + +

Результат выглядит так:

+ +

{{EmbedLiveSample("Пример_strokeStyle", "180", "180", "https://mdn.mozillademos.org/files/253/Canvas_strokestyle.png")}}

+ +

Прозрачность

+ +

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

+ +
+
{{domxref("CanvasRenderingContext2D.globalAlpha", "globalAlpha = transparencyValue")}}
+
Для применения, указывается значения прозрачности для всех будущих фигур, что будут нарисованы на canvas. Значение полупрозрачности могут быть между 0.0 (полная прозрачность) и 1.0 (полная непрозрачность). Значение 1.0 (полная непрозрачность) установлено по умолчанию.
+
+ +

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

+ +

Так как свойства strokeStyle и fillStyle принимают цветовые значения rgba через CSS, мы можем использовать следующее обозначение  для назначения прозрачных цветов.

+ +
// Assigning transparent colors to stroke and fill style
+
+ctx.strokeStyle = "rgba(255,0,0,0.5)";
+ctx.fillStyle = "rgba(255,0,0,0.5)";
+
+ +

Функция rgba() похожа на функцию rgb(), но имеет один дополнительный параметр. Последний параметр устанавливает значение прозрачности для конкретного цвета. Действующий диапозон значений находится между 0.0 (полная прозрачность) и 1.0 (полная непрозрачность).

+ +

Пример globalAlpha

+ +

В данном примере мы нарисуем фон и четыре квадрата с различными цветами.  Сверху изображения будет выведен набор полупрозрачных кругов. Установим свойство globalAlpha значением 0.2, которое будет использовано для всех последующих форм. Каждый шаг цикла рисует круг с большим радиусом. По окончанию получим радиальный градиент. Накладывая еще больше кругов друг на друга, мы фактически сможем уменьшить прозрачность ранее нарисованных кругов. Увеличив счетчик итераций, при этом рисуя еще круги, мы сможем добиться исчезновение центра изображения.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  // фон изображения
+  ctx.fillStyle = '#FD0';
+  ctx.fillRect(0,0,75,75);
+  ctx.fillStyle = '#6C0';
+  ctx.fillRect(75,0,75,75);
+  ctx.fillStyle = '#09F';
+  ctx.fillRect(0,75,75,75);
+  ctx.fillStyle = '#F30';
+  ctx.fillRect(75,75,75,75);
+  ctx.fillStyle = '#FFF';
+
+  // устанавливаем значение прозрачности
+  ctx.globalAlpha = 0.2;
+
+  // Рисуем полупрозрачные круги
+  for (i=0;i<7;i++){
+    ctx.beginPath();
+    ctx.arc(75,75,10+10*i,0,Math.PI*2,true);
+    ctx.fill();
+  }
+}
+ + + +

{{EmbedLiveSample("Пример_globalAlpha", "180", "180", "https://mdn.mozillademos.org/files/232/Canvas_globalalpha.png")}}

+ +

Пример использования rgba()

+ +

В этом втором примере мы делаем что-то похожее на предыдущее, но вместо рисования кругов друг над другом, я рисовал маленькие прямоугольники с увеличением непрозрачности. Использование rgba() добавляет контроля и гибкости, поскольку мы можем индивидуально настраивать стиль заливки и штриха.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Нарисовать фон
+  ctx.fillStyle = 'rgb(255,221,0)';
+  ctx.fillRect(0,0,150,37.5);
+  ctx.fillStyle = 'rgb(102,204,0)';
+  ctx.fillRect(0,37.5,150,37.5);
+  ctx.fillStyle = 'rgb(0,153,255)';
+  ctx.fillRect(0,75,150,37.5);
+  ctx.fillStyle = 'rgb(255,51,0)';
+  ctx.fillRect(0,112.5,150,37.5);
+
+  // Нарисовать полупрозрачные прямоугольники
+  for (var i=0;i<10;i++){
+    ctx.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')';
+    for (var j=0;j<4;j++){
+      ctx.fillRect(5+i*14,5+j*37.5,14,27.5);
+    }
+  }
+}
+ + + +

{{EmbedLiveSample("Пример_использования_rgba()", "180", "180", "https://mdn.mozillademos.org/files/246/Canvas_rgba.png")}}

+ +

Стили линий

+ +

Есть несколько свойств, которые позволяют нам стилизовать линии.

+ +
+
{{domxref("CanvasRenderingContext2D.lineWidth", "lineWidth = value")}}
+
Устанавливает ширину линий, рисуемых в будущем.
+
{{domxref("CanvasRenderingContext2D.lineCap", "lineCap = type")}}
+
Устанавливает внешний вид концов линий.
+
{{domxref("CanvasRenderingContext2D.lineJoin", "lineJoin = type")}}
+
Устанавливает внешний вид «углов», где встречаются линии.
+
{{domxref("CanvasRenderingContext2D.miterLimit", "miterLimit = value")}}
+
Устанавливает ограничение на митру, когда две линии соединяются под острым углом, чтобы вы могли контролировать её толщину.
+
{{domxref("CanvasRenderingContext2D.getLineDash", "getLineDash()")}}
+
Возвращает текущий массив тире штриховки, содержащий четное число неотрицательных чисел.
+
{{domxref("CanvasRenderingContext2D.setLineDash", "setLineDash(segments)")}}
+
Устанавливает текущий пунктир линии.
+
{{domxref("CanvasRenderingContext2D.lineDashOffset", "lineDashOffset = value")}}
+
Указывает, где следует начинать тире массива в строке.
+
+ +

Вы лучше поймете, что они делают, глядя на приведенные ниже примеры.

+ +

Пример lineWidth

+ +

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

+ +

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

+ +

В приведенном ниже примере 10 прямых линий рисуются с увеличением ширины линий. Линия в крайнем левом углу - 1.0 единицы. Тем не менее, толщина левой и всех других линий нечетной ширины не выглядят четкими из-за позиционирования пути.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  for (var i = 0; i < 10; i++){
+    ctx.lineWidth = 1+i;
+    ctx.beginPath();
+    ctx.moveTo(5+i*14,5);
+    ctx.lineTo(5+i*14,140);
+    ctx.stroke();
+  }
+}
+
+ + + +

{{EmbedLiveSample("Пример_lineWidth", "180", "180", "https://mdn.mozillademos.org/files/239/Canvas_linewidth.png")}}

+ +

Получение четких строк требует понимания путей сглаживания. На рисунках ниже представлена сетка координат холста. Квадраты между сетками являются фактическими экранными пикселями. В первом изображении сетки ниже прямоугольник от (2, 1) до (5, 5) заполняется. Вся область между ними (светло-красный) падает на границы пикселей, поэтому полученный заполненный прямоугольник будет иметь четкие края.

+ +

+ +

Если вы рассмотрите путь от (3, 1) до (3, 5) с толщиной строки 1.0, вы получите ситуацию во втором изображении. Фактическая заполняемая область, (синяя), распространяется только наполовину в пикселях по обе стороны пути. Приблизительно это означает, что частично затенённые пиксели приводят к заполнению всей области (светло-голубой и синей) цветом, только наполовину темным, чем фактический цвет штриха. Это то, что происходит с линией шириной 1.0 в предыдущем примере кода.

+ +

Чтобы исправить это, вы должны быть более точными при создании пути. Зная, что линия шириной 1.0 занимает половину единицы по обе стороны пути, создание пути от (3.5, 1) до (3.5, 5) приведёт к ситуации в третьем изображении - ширина линии 1.0 закончится верно, точно заполняя вертикальную линию с одним пикселем.

+ +
+

Примечание: Имейте в виду, что в нашем примере с вертикальной линией позиция Y по-прежнему ссылается на целочисленную позицию сетки - иначе мы увидели бы пиксели с половинным охватом в конечных точках (также обратите внимание, что это поведение зависит от текущего стиля lineCap,  значение по умолчанию - butt; вы можете вычислить согласованные штрихи с полупиксельными координатами для линий с нечетной шириной, установив стиль lineCap в square, чтобы внешняя граница вокруг конечной точки линии автоматически расширялась, охватывая весь пиксель в точку).

+ +

Также обратите внимание, что затронуты только начальные и конечные  точки пути: если путь закрыт с помощью closePath(), - нет начальной и конечной точки; вместо этого все конечные точки в пути подключены к их прикрепленному предыдущему и следующему сегментам и при текущей настройке стиля lineJoin в значении по умолчанию - miter, с эффектом автоматического расширения внешних границ подключенных сегментов до их точки пересечения - обработанный ход будет точно покрывать полные пиксели с центром в каждой конечной точке, если эти связанные сегменты горизонтальны и/или вертикальны). См. следующие два раздела, демонстрирующие эти дополнительные стили.

+
+ +

Для линий с четной шириной каждая половина заканчивается как целое количество пикселей, поэтому вам нужен путь, который находится между пикселями (то есть (3,1) - (3,5)), вместо середины пикселей.

+ +

Хотя это и необычно, когда изначально работаешь с масштабируемой 2D-графикой, обращая внимание на сетку пикселей и положение путей, но вы убедитесь, что ваши рисунки будут выглядеть правильно, независимо от масштабирования или любых других преобразований. Вертикальная линия ширины 1,0, построенная таким образом, станет четкой 2-пиксельной линией при увеличении на 2 и появится в правильном положении.

+ +

Пример lineCap

+ +

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

+ +

+ +
+
butt
+
Концы линий соответствуют крайним точкам.
+
round
+
Концы линий округлены.
+
square
+
Концы линий описаны квадратом с равной шириной и половиной высоты толщины линии.
+
+ +

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

+ +

Строка слева использует butt опцию по умолчанию. Вы заметите, что она полностью очищена от направляющих. Второй вариант -  round опция. Это добавляет полукруг к концу, который имеет радиус, равный половине ширины линии. Строка справа использует square опцию. Это добавляет поле с равной шириной и половиной высоты толщины линии.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var lineCap = ['butt','round','square'];
+
+  // Draw guides
+  ctx.strokeStyle = '#09f';
+  ctx.beginPath();
+  ctx.moveTo(10,10);
+  ctx.lineTo(140,10);
+  ctx.moveTo(10,140);
+  ctx.lineTo(140,140);
+  ctx.stroke();
+
+  // Draw lines
+  ctx.strokeStyle = 'black';
+  for (var i=0;i<lineCap.length;i++){
+    ctx.lineWidth = 15;
+    ctx.lineCap = lineCap[i];
+    ctx.beginPath();
+    ctx.moveTo(25+i*50,10);
+    ctx.lineTo(25+i*50,140);
+    ctx.stroke();
+  }
+}
+
+ + + +

{{EmbedLiveSample("Пример_lineCap", "180", "180", "https://mdn.mozillademos.org/files/236/Canvas_linecap.png")}}

+ +

Пример lineJoin

+ +

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

+ +

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

+ +

+ +
+
round
+
Радиус заполняемой части для скругленных углов равен половине ширины линии. центр этого радиуса совпадает с концами подключенных сегментов.
+
bevel
+
Заполняет дополнительную треугольную область между общей конечной точкой подключенных сегментов и отдельными внешними прямоугольными углами каждого сегмента. 
+
miter
+
Подключенные сегменты соединяются путем расширения их внешних краев для соединения в одной точке с эффектом заполнения дополнительной области в форме пастилки. Эта настройка выполняется с помощью свойства miterLimit, которое объясняется ниже.
+
+ +

В приведенном ниже примере показаны три разных пути, демонстрирующие каждый из этих трех свойств lineJoin; результат - выше. 

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var lineJoin = ['round','bevel','miter'];
+  ctx.lineWidth = 10;
+  for (var i=0;i<lineJoin.length;i++){
+    ctx.lineJoin = lineJoin[i];
+    ctx.beginPath();
+    ctx.moveTo(-5,5+i*40);
+    ctx.lineTo(35,45+i*40);
+    ctx.lineTo(75,5+i*40);
+    ctx.lineTo(115,45+i*40);
+    ctx.lineTo(155,5+i*40);
+    ctx.stroke();
+  }
+}
+
+ + + +

{{EmbedLiveSample("Пример_lineJoin", "180", "180", "https://mdn.mozillademos.org/files/237/Canvas_linejoin.png")}}

+ +

Демонстрация свойства miterLimit

+ +

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

+ +

Свойство miterLimit определяет, как далеко можно установить внешнюю точку соединения из внутренней точки подключения. Если две линии превышают это значение, вместо этого получается привязка конуса. Обратите внимание, что максимальная длина митра является произведением ширины линии, измеренной в текущей системе координат, значением этого свойства miterLimit (значение по умолчанию 10,0 в HTML {{HTMLElement("canvas")}}), поэтому miterLimit может устанавливаться независимо от текущей шкалы дисплея или любых аффинных преобразований путей: она влияет только на эффективно визуализированную форму ребер линии.

+ +

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

+ + + +

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

+ +

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

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Clear canvas
+  ctx.clearRect(0,0,150,150);
+
+  // Draw guides
+  ctx.strokeStyle = '#09f';
+  ctx.lineWidth   = 2;
+  ctx.strokeRect(-5,50,160,50);
+
+  // Set line styles
+  ctx.strokeStyle = '#000';
+  ctx.lineWidth = 10;
+
+  // check input
+  if (document.getElementById('miterLimit').value.match(/\d+(\.\d+)?/)) {
+    ctx.miterLimit = parseFloat(document.getElementById('miterLimit').value);
+  } else {
+    alert('Value must be a positive number');
+  }
+
+  // Draw lines
+  ctx.beginPath();
+  ctx.moveTo(0,100);
+  for (i=0;i<24;i++){
+    var dy = i%2==0 ? 25 : -25 ;
+    ctx.lineTo(Math.pow(i,1.5)*2,75+dy);
+  }
+  ctx.stroke();
+  return false;
+}
+
+ + + +

{{EmbedLiveSample("Демонстрация_свойства_miterLimit", "400", "180", "https://mdn.mozillademos.org/files/240/Canvas_miterlimit.png")}}

+ +

Использование штрихов

+ +

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

+ +

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

+ + + +
var ctx = document.getElementById('canvas').getContext('2d');
+var offset = 0;
+
+function draw() {
+  ctx.clearRect(0,0, canvas.width, canvas.height);
+  ctx.setLineDash([4, 2]);
+  ctx.lineDashOffset = -offset;
+  ctx.strokeRect(10,10, 100, 100);
+}
+
+function march() {
+  offset++;
+  if (offset > 16) {
+    offset = 0;
+  }
+  draw();
+  setTimeout(march, 20);
+}
+
+march();
+ +

{{EmbedLiveSample("Используемый штрих", "120", "120", "https://mdn.mozillademos.org/files/9853/marching-ants.png")}}

+ +

Градиенты

+ +

Just like any normal drawing program, we can fill and stroke shapes using linear and radial gradients. We create a {{domxref("CanvasGradient")}} object by using one of the following methods. We can then assign this object to the fillStyle or strokeStyle properties.

+ +
+
{{domxref("CanvasRenderingContext2D.createLinearGradient", "createLinearGradient(x1, y1, x2, y2)")}}
+
Creates a linear gradient object with a starting point of (x1, y1) and an end point of (x2, y2).
+
{{domxref("CanvasRenderingContext2D.createRadialGradient", "createRadialGradient(x1, y1, r1, x2, y2, r2)")}}
+
Creates a radial gradient. The parameters represent two circles, one with its center at (x1, y1) and a radius of r1, and the other with its center at (x2, y2) with a radius of r2.
+
+ +

For example:

+ +
var lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
+var radialgradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100);
+
+ +

Once we've created a CanvasGradient object we can assign colors to it by using the addColorStop() method.

+ +
+
{{domxref("CanvasGradient.addColorStop", "gradient.addColorStop(position, color)")}}
+
Creates a new color stop on the gradient object. The position is a number between 0.0 and 1.0 and defines the relative position of the color in the gradient, and the color argument must be a string representing a CSS {{cssxref("<color>")}}, indicating the color the gradient should reach at that offset into the transition.
+
+ +

You can add as many color stops to a gradient as you need. Below is a very simple linear gradient from white to black.

+ +
var lineargradient = ctx.createLinearGradient(0,0,150,150);
+lineargradient.addColorStop(0, 'white');
+lineargradient.addColorStop(1, 'black');
+
+ +

Пример createLinearGradient

+ +

In this example, we'll create two different gradients. As you can see here, both the strokeStyle and fillStyle properties can accept a canvasGradient object as valid input.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Create gradients
+  var lingrad = ctx.createLinearGradient(0,0,0,150);
+  lingrad.addColorStop(0, '#00ABEB');
+  lingrad.addColorStop(0.5, '#fff');
+  lingrad.addColorStop(0.5, '#26C000');
+  lingrad.addColorStop(1, '#fff');
+
+  var lingrad2 = ctx.createLinearGradient(0,50,0,95);
+  lingrad2.addColorStop(0.5, '#000');
+  lingrad2.addColorStop(1, 'rgba(0,0,0,0)');
+
+  // assign gradients to fill and stroke styles
+  ctx.fillStyle = lingrad;
+  ctx.strokeStyle = lingrad2;
+
+  // draw shapes
+  ctx.fillRect(10,10,130,130);
+  ctx.strokeRect(50,50,50,50);
+
+}
+
+ + + +

The first is a background gradient. As you can see, we assigned two colors at the same position. You do this to make very sharp color transitions—in this case from white to green. Normally, it doesn't matter in what order you define the color stops, but in this special case, it does significantly. If you keep the assignments in the order you want them to appear, this won't be a problem.

+ +

In the second gradient, we didn't assign the starting color (at position 0.0) since it wasn't strictly necessary, because it will automatically assume the color of the next color stop. Therefore, assigning the black color at position 0.5 automatically makes the gradient, from the start to this stop, black.

+ +

{{EmbedLiveSample("Пример_createLinearGradient", "180", "180", "https://mdn.mozillademos.org/files/235/Canvas_lineargradient.png")}}

+ +

Пример createRadialGradient

+ +

In this example, we'll define four different radial gradients. Because we have control over the start and closing points of the gradient, we can achieve more complex effects than we would normally have in the "classic" radial gradients we see in, for instance, Photoshop (that is, a gradient with a single center point where the gradient expands outward in a circular shape).

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Create gradients
+  var radgrad = ctx.createRadialGradient(45,45,10,52,50,30);
+  radgrad.addColorStop(0, '#A7D30C');
+  radgrad.addColorStop(0.9, '#019F62');
+  radgrad.addColorStop(1, 'rgba(1,159,98,0)');
+
+  var radgrad2 = ctx.createRadialGradient(105,105,20,112,120,50);
+  radgrad2.addColorStop(0, '#FF5F98');
+  radgrad2.addColorStop(0.75, '#FF0188');
+  radgrad2.addColorStop(1, 'rgba(255,1,136,0)');
+
+  var radgrad3 = ctx.createRadialGradient(95,15,15,102,20,40);
+  radgrad3.addColorStop(0, '#00C9FF');
+  radgrad3.addColorStop(0.8, '#00B5E2');
+  radgrad3.addColorStop(1, 'rgba(0,201,255,0)');
+
+  var radgrad4 = ctx.createRadialGradient(0,150,50,0,140,90);
+  radgrad4.addColorStop(0, '#F4F201');
+  radgrad4.addColorStop(0.8, '#E4C700');
+  radgrad4.addColorStop(1, 'rgba(228,199,0,0)');
+
+  // draw shapes
+  ctx.fillStyle = radgrad4;
+  ctx.fillRect(0,0,150,150);
+  ctx.fillStyle = radgrad3;
+  ctx.fillRect(0,0,150,150);
+  ctx.fillStyle = radgrad2;
+  ctx.fillRect(0,0,150,150);
+  ctx.fillStyle = radgrad;
+  ctx.fillRect(0,0,150,150);
+}
+
+ + + +

In this case, we've offset the starting point slightly from the end point to achieve a spherical 3D effect. It's best to try to avoid letting the inside and outside circles overlap because this results in strange effects which are hard to predict.

+ +

The last color stop in each of the four gradients uses a fully transparent color. If you want to have a nice transition from this to the previous color stop, both colors should be equal. This isn't very obvious from the code because it uses two different CSS color methods as a demonstration, but in the first gradient #019F62 = rgba(1,159,98,1).

+ +

{{EmbedLiveSample("Пример_createRadialGradient", "180", "180", "https://mdn.mozillademos.org/files/244/Canvas_radialgradient.png")}}

+ +

Шаблоны

+ +

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

+ +
+
{{domxref("CanvasRenderingContext2D.createPattern", "createPattern(image, type)")}}
+
Создает и возвращает новый canvas объект - шаблон (pattern). image - {{domxref("CanvasImageSource")}} (то есть {{domxref ("HTMLImageElement")}}, другой холст, элемент {{HTMLElement ("video")}} или подобный  объект. type - строка, указывающая, как использовать image.
+
+ +

Тип указывает, как использовать image для создания шаблона и должен быть одним из следующих значений:

+ +
+
repeat
+
Повторяет изображение в вертикальном и горизонтальном направлениях.
+
repeat-x
+
Повторяет изображение по горизонтали, но не по вертикали.
+
repeat-y
+
Повторяет изображение по вертикали, но не по горизонтали.
+
no-repeat
+
Не повторяет изображение. Используется только один раз.
+
+ +

Мы используем этот метод, чтобы создать {{domxref("CanvasPattern")}} объект, который очень похож на методы градиента, рассмотренные ранее. Как только мы создали шаблон, мы можем назначить ему свойства fillStyle или strokeStyle. Например:

+ +
var img = new Image();
+img.src = 'someimage.png';
+var ptrn = ctx.createPattern(img,'repeat');
+
+ +
+

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

+
+ +

Пример createPattern

+ +

In this last example, we'll create a pattern to assign to the fillStyle property. The only thing worth noting is the use of the image's onload handler. This is to make sure the image is loaded before it is assigned to the pattern.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // create new image object to use as pattern
+  var img = new Image();
+  img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
+  img.onload = function(){
+
+    // create pattern
+    var ptrn = ctx.createPattern(img,'repeat');
+    ctx.fillStyle = ptrn;
+    ctx.fillRect(0,0,150,150);
+
+  }
+}
+
+ + + +

{{EmbedLiveSample("Пример_createPattern", "180", "180", "https://mdn.mozillademos.org/files/222/Canvas_createpattern.png")}}

+ +

Тени

+ +

Using shadows involves just four properties:

+ +
+
{{domxref("CanvasRenderingContext2D.shadowOffsetX", "shadowOffsetX = float")}}
+
Indicates the horizontal distance the shadow should extend from the object. This value isn't affected by the transformation matrix. The default is 0.
+
{{domxref("CanvasRenderingContext2D.shadowOffsetY", "shadowOffsetY = float")}}
+
Indicates the vertical distance the shadow should extend from the object. This value isn't affected by the transformation matrix. The default is 0.
+
{{domxref("CanvasRenderingContext2D.shadowBlur", "shadowBlur = float")}}
+
Indicates the size of the blurring effect; this value doesn't correspond to a number of pixels and is not affected by the current transformation matrix. The default value is 0.
+
{{domxref("CanvasRenderingContext2D.shadowColor", "shadowColor = color")}}
+
A standard CSS color value indicating the color of the shadow effect; by default, it is fully-transparent black.
+
+ +

The properties shadowOffsetX and shadowOffsetY indicate how far the shadow should extend from the object in the X and Y directions; these values aren't affected by the current transformation matrix. Use negative values to cause the shadow to extend up or to the left, and positive values to cause the shadow to extend down or to the right. These are both 0 by default.

+ +

The shadowBlur property indicates the size of the blurring effect; this value doesn't correspond to a number of pixels and is not affected by the current transformation matrix. The default value is 0.

+ +

The shadowColor property is a standard CSS color value indicating the color of the shadow effect; by default, it is fully-transparent black.

+ +
+

Note: Shadows are only drawn for source-over compositing operations.

+
+ +

Пример текста с тенью

+ +

This example draws a text string with a shadowing effect.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  ctx.shadowOffsetX = 2;
+  ctx.shadowOffsetY = 2;
+  ctx.shadowBlur = 2;
+  ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
+
+  ctx.font = "20px Times New Roman";
+  ctx.fillStyle = "Black";
+  ctx.fillText("Sample String", 5, 30);
+}
+
+ + + +

{{EmbedLiveSample("Пример_текста_с_тенью", "180", "100", "https://mdn.mozillademos.org/files/2505/shadowed-string.png")}}

+ +

We will look at the font property and fillText method in the next chapter about drawing text.

+ +

Canvas fill rules

+ +

When using fill (or {{domxref("CanvasRenderingContext2D.clip", "clip")}} and {{domxref("CanvasRenderingContext2D.isPointInPath", "isPointinPath")}}) you can optionally provide a fill rule algorithm by which to determine if a point is inside or outside a path and thus if it gets filled or not. This is useful when a path intersetcs itself or is nested.
+
+ Two values are possible:

+ + + +

In this example we are using the evenodd rule.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.beginPath();
+  ctx.arc(50, 50, 30, 0, Math.PI*2, true);
+  ctx.arc(50, 50, 15, 0, Math.PI*2, true);
+  ctx.fill("evenodd");
+}
+ + + +

{{EmbedLiveSample("Canvas_fill_rules", "110", "110", "https://mdn.mozillademos.org/files/9855/fill-rule.png")}}

+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_shapes", "Web/API/Canvas_API/Tutorial/Drawing_text")}}

diff --git "a/files/ru/web/api/canvas_api/tutorial/\321\200\320\270\321\201\320\276\320\262\320\260\320\275\320\270\320\265_\321\202\320\265\320\272\321\201\321\202\320\260/index.html" "b/files/ru/web/api/canvas_api/tutorial/\321\200\320\270\321\201\320\276\320\262\320\260\320\275\320\270\320\265_\321\202\320\265\320\272\321\201\321\202\320\260/index.html" new file mode 100644 index 0000000000..90915c5e09 --- /dev/null +++ "b/files/ru/web/api/canvas_api/tutorial/\321\200\320\270\321\201\320\276\320\262\320\260\320\275\320\270\320\265_\321\202\320\265\320\272\321\201\321\202\320\260/index.html" @@ -0,0 +1,166 @@ +--- +title: Рисование текста +slug: Web/API/Canvas_API/Tutorial/Рисование_текста +tags: + - Canvas + - Графика + - Примеры + - Рукводовство + - мануал +translation_of: Web/API/Canvas_API/Tutorial/Drawing_text +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Applying_styles_and_colors", "Web/API/Canvas_API/Tutorial/Using_images")}}
+ +
+

После того, как мы увидели в предыдущей главе, как применять стили и цвета, взглянем на написание текста в canvas.

+
+ +

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

+ +

Контекст рендеринга canvas предоставляет два метода для рисования текста:

+ +
+
{{domxref("CanvasRenderingContext2D.fillText", "fillText(text, x, y [, maxWidth])")}}
+
Вставляет заданный текст в положении (x,y). Опционально может быть указана максимальная ширина.
+
{{domxref("CanvasRenderingContext2D.strokeText", "strokeText(text, x, y [, maxWidth])")}}
+
Вставляет контур заданного текста в положении (x,y). Опционально может быть указана максимальная ширина.
+
+ +

Пример fillText

+ +

Текст вставлен с использованием текущего fillStyle.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.font = "48px serif";
+  ctx.fillText("Hello world", 10, 50);
+}
+ + + +

{{EmbedLiveSample("A_fillText_example", 310, 110)}}

+ +

Пример strokeText

+ +

Текст вставлен с использованием текущего strokeStyle.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.font = "48px serif";
+  ctx.strokeText("Hello world", 10, 50);
+}
+ + + +

{{EmbedLiveSample("A_strokeText_example", 310, 110)}}

+ +

Стилизация текста

+ +

В примерах выше мы уже использовали свойство font для изменения размера текста. Кроме него существуют еще несколько свойств, позволяющие настроить вывод текста на canvas:

+ +
+
{{domxref("CanvasRenderingContext2D.font", "font = value")}}
+
Это основной стиль, который будет использоваться для вывода текста. Строка имеет такой же синтаксис, как CSS-свойство {{cssxref("font")}}. По умолчанию - sans-serif высотой 10px.
+
{{domxref("CanvasRenderingContext2D.textAlign", "textAlign = value")}}
+
Настройка выравнивания текста. Возможные значения: start, end, left, right или center. По умолчанию - start.
+
{{domxref("CanvasRenderingContext2D.textBaseline", "textBaseline = value")}}
+
Настройка выравнивания текста по вертикали. Возможные значения: top, hanging, middle, alphabetic, ideographic, bottom. По умолчанию - alphabetic.
+
{{domxref("CanvasRenderingContext2D.direction", "direction = value")}}
+
Направление текста. Возможные значения: ltr, rtl, inherit. По умолчанию - inherit.
+
+ +

Эти свойства могут быть вам знакомы если вы работали с CSS.

+ +

Изображение от WHATWG ниже показывает различные варианты свойства textBaseline.The top of the em square is
+roughly at the top of the glyphs in a font, the hanging baseline is
+where some glyphs like आ are anchored, the middle is half-way
+between the top of the em square and the bottom of the em square,
+the alphabetic baseline is where characters like Á, ÿ,
+f, and Ω are anchored, the ideographic baseline is
+where glyphs like 私 and 達 are anchored, and the bottom
+of the em square is roughly at the bottom of the glyphs in a
+font. The top and bottom of the bounding box can be far from these
+baselines, due to glyphs extending far outside the em square.

+ +

Пример textBaseline

+ +

Редактируя код ниже, вы можете видеть, как меняется отображение текста на canvas в реальном времени:

+ +
ctx.font = "48px serif";
+ctx.textBaseline = "hanging";
+ctx.strokeText("Hello world!", 0, 100);
+
+ + + +

{{ EmbedLiveSample('Playable_code', 700, 360) }}

+ +

Измерение ширины текста

+ +

Для измерения ширины текста (без рисования его на canvas) можно воспользоваться следующим методом:

+ +
+
{{domxref("CanvasRenderingContext2D.measureText", "measureText()")}}
+
Возвращает объект {{domxref("TextMetrics")}}, содержащий ширину текста в пикселах, до отрисовки на canvas.
+
+ +

Пример ниже показывает, как можно измерить ширину текста.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var text = ctx.measureText("foo"); // TextMetrics object
+  text.width; // 16;
+}
+
+ +

Примечания

+ +

В ранних версиях Gecko (движок рендеринга в Firefox, Firefox OS и других приложениях Mozilla) были реализованы методы API с префиксами для рисования текста на canvas. На данный момент они устарели и уже, возможно, удалены, поэтому их правильная работа не гарантируется.

+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Applying_styles_and_colors", "Web/API/Canvas_API/Tutorial/Using_images")}}

diff --git "a/files/ru/web/api/canvas_api/tutorial/\321\200\320\270\321\201\320\276\320\262\320\260\320\275\320\270\320\265_\321\204\320\270\320\263\321\203\321\200/index.html" "b/files/ru/web/api/canvas_api/tutorial/\321\200\320\270\321\201\320\276\320\262\320\260\320\275\320\270\320\265_\321\204\320\270\320\263\321\203\321\200/index.html" new file mode 100644 index 0000000000..f6ca6c23ef --- /dev/null +++ "b/files/ru/web/api/canvas_api/tutorial/\321\200\320\270\321\201\320\276\320\262\320\260\320\275\320\270\320\265_\321\204\320\270\320\263\321\203\321\200/index.html" @@ -0,0 +1,582 @@ +--- +title: Рисование фигур с помощью canvas +slug: Web/API/Canvas_API/Tutorial/Рисование_фигур +translation_of: Web/API/Canvas_API/Tutorial/Drawing_shapes +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_usage", "Web/API/Canvas_API/Tutorial/Applying_styles_and_colors")}}
+ +
+

Теперь, установив наше окружение canvas, мы можем погрузиться в детали того, как рисовать в canvas. К концу этой статьи, Вы научитесь рисовать прямоугольники, треугольники, линии, дуги и кривые, при условии что Вы хорошо знакомы с основными геометрическими фигурами. Работа с путями весьма важна, когда рисуете объекты на canvas и мы увидим как это может быть сделано.

+
+ +

Сетка

+ +

Перед тем, как мы начнем рисовать, нам нужно поговорить о сетке canvas или координатной плоскости. Наш HTML каркас из предыдущей страницы включал в себя элемент canvas 150 пикселей в ширину и 150 пикселей в высоту. Справа можно увидеть этот canvas с сеткой, накладываемой по умолчанию. Обычно 1 единица на сетке соответствует 1 пикселю на canvas. Начало координат этой сетки расположено в верхнем левом углу в координате (0,0 ). Все элементы размещены относительно этого начала. Таким образом, положение верхнего левого угла синего квадрата составляет х пикселей слева и у пикселей сверху, на координате , у). Позже в этом уроке мы увидим, как можно перевести начало координат в другое место, вращать сетку и даже масштабировать ее, но сейчас мы будем придерживаться настроек сетки по умолчанию.

+ +

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

+ +

В отличие от {{Glossary("SVG")}}, {{HTMLElement("canvas")}} поддерживает только одну примитивную фигуру: прямоугольник. Все другие фигуры должны быть созданы комбинацией одного или большего количества контуров (paths), набором точек, соединенных в линии. К счастью в ассортименте рисования контуров у нас есть  функции, которые делают возможным составление очень сложных фигур.

+ +

Сначала рассмотрим прямоугольник. Ниже представлены три функции рисования прямоугольников в canvas:

+ +
+
{{domxref("CanvasRenderingContext2D.fillRect", "fillRect(x, y, width, height)")}}
+
Рисование заполненного прямоугольника.
+
{{domxref("CanvasRenderingContext2D.strokeRect", "strokeRect(x, y, width, height)")}}
+
Рисование прямоугольного контура.
+
{{domxref("CanvasRenderingContext2D.clearRect", "clearRect(x, y, width, height)")}}
+
Очистка  прямоугольной области, делая содержимое совершенно прозрачным.
+
+ +

Каждая из приведенных функций принимает несколько параметров: 

+ + + +

Ниже приведена функция draw(), использующая эти три функции.

+ +

Пример создания прямоугольных фигур

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    ctx.fillRect(25,25,100,100);
+    ctx.clearRect(45,45,60,60);
+    ctx.strokeRect(50,50,50,50);
+  }
+}
+ +

Этот пример изображен ниже.

+ +

{{EmbedLiveSample("Пример_создания_прямоугольных_фигур", 160, 160, "https://mdn.mozillademos.org/files/245/Canvas_rect.png")}}

+ +

Функция fillRect() рисует большой чёрный квадрат со стороной 100 px. Функция clearRect() вырезает квадрат 60х60 из центра, а функция strokeRect() создает прямоугольный контур 50х50 пикселей внутри очищенного квадрата.

+ +

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

+ +

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

+ +

Рисование контуров (path)

+ +

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

+ +

Создание фигур используя контуры происходит в несколько важных шагов:

+ +
    +
  1. Сначала вы создаете контур.
  2. +
  3. Затем, используя команды рисования, рисуете контур.
  4. +
  5. Потом закрываете контур.
  6. +
  7. Созданный контур вы можете обвести или залить для его отображения.
  8. +
+ +

Здесь приведены функции, которые можно использовать в описанных шагах:

+ +
+
{{domxref("CanvasRenderingContext2D.beginPath", "beginPath()")}}
+
Создает новый контур. После создания используется в дальнейшем командами рисования при построении контуров.
+
Path методы
+
Методы для установки различных контуров объекта.
+
{{domxref("CanvasRenderingContext2D.closePath", "closePath()")}}
+
Закрывает контур, так что будущие команды рисования вновь направлены контекст.
+
{{domxref("CanvasRenderingContext2D.stroke", "stroke()")}}
+
Рисует фигуру с внешней обводкой.
+
{{domxref("CanvasRenderingContext2D.fill", "fill()")}}
+
Рисует фигуру с заливкой внутренней области.
+
+ +

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

+ +
Note:  если текущий контур пуст (например, как после вызова beginPath() или на вновь созданном canvas), первой командой построения контура всегда является функция  moveTo(). Поэтому мы всегда можем установить начальную позицию рисования контура после перезагрузки.
+ +

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

+ +

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

+ +
Note: Когда вы вызываете fill(), то каждая открытая фигура закрывается автоматически, так что вы можете не использовать closePath(). Это обстоятельство не имеет место в случае вызова stroke().
+ +

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

+ +

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

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    ctx.beginPath();
+    ctx.moveTo(75,50);
+    ctx.lineTo(100,75);
+    ctx.lineTo(100,25);
+    ctx.fill();
+  }
+}
+
+ +

Результат выглядит так:

+ +

{{EmbedLiveSample("Рисование_треугольника", 110, 110, "https://mdn.mozillademos.org/files/9847/triangle.png")}}

+ +

Передвижение пера

+ +

Одна очень полезная функция, которая ничего не рисует, но связана по смыслу с вышеописанными функциями  - это moveTo(). Вы можете представить это как отрыв (подъем) пера от бумаги и его перемещение в другое место.

+ +
+
{{domxref("CanvasRenderingContext2D.moveTo", "moveTo(x, y)")}}
+
Перемещает перо в точку с координатами x и y.
+
+ +

При инициализации canvas или при вызове beginPath(), вы захотите использовать функцию moveTo() для перемещения в точку начала рисования. Можно использовать moveTo() и для рисования несвязанного(незакрытого) контура. Посмотрите на смайлик ниже.

+ +

Вы можете проверить это сами, используя участок кода ниже. Просто вставьте в функцию draw(), рассмотренную ранее.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+     var ctx = canvas.getContext('2d');
+
+    ctx.beginPath();
+    ctx.arc(75,75,50,0,Math.PI*2,true); // Внешняя окружность
+    ctx.moveTo(110,75);
+    ctx.arc(75,75,35,0,Math.PI,false);  // рот (по часовой стрелке)
+    ctx.moveTo(65,65);
+    ctx.arc(60,65,5,0,Math.PI*2,true);  // Левый глаз
+    ctx.moveTo(95,65);
+    ctx.arc(90,65,5,0,Math.PI*2,true);  // Правый глаз
+    ctx.stroke();
+  }
+}
+
+ +

Результат этого ниже:

+ +

{{EmbedLiveSample("Передвижение_пера", 160, 160, "https://mdn.mozillademos.org/files/252/Canvas_smiley.png")}}

+ +

Если вы захотите увидеть соединные линии, то можете удалить вызов moveTo().

+ +
+

Note: Подробнее о функции arc(),посмотрите {{anch("Дуги")}} .

+
+ +

Линии

+ +

Для рисования прямых линий используйте метод lineTo().

+ +
+
{{domxref("CanvasRenderingContext2D.lineTo", "lineTo(x, y)")}}
+
Рисует линию с текущей позиции до позиции, определенной x и y.
+
+ +

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

+ +

Пример ниже рисует два треугольника, один закрашенный и другой обведен контуром.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    // Filled triangle
+    ctx.beginPath();
+    ctx.moveTo(25,25);
+    ctx.lineTo(105,25);
+    ctx.lineTo(25,105);
+    ctx.fill();
+
+    // Stroked triangle
+    ctx.beginPath();
+    ctx.moveTo(125,125);
+    ctx.lineTo(125,45);
+    ctx.lineTo(45,125);
+    ctx.closePath();
+    ctx.stroke();
+  }
+}
+
+ +

Отрисовка начинается с вызова beginPath(), чтобы начать рисовать путь новой фигуры. Затем мы используем метод moveTo(), чтобы переместить начальную точку в нужное положение. Ниже рисуются две линии, которые образуют две стороны треугольника.

+ +

{{EmbedLiveSample("Линии", 160, 160, "https://mdn.mozillademos.org/files/238/Canvas_lineTo.png")}}

+ +

Вы заметите разницу между закрашенным и обведенным контуром треугольниками. Это, как упоминалось выше, из-за того, что фигуры автоматически закрываются, когда путь заполнен (т. е. закрашен), но не тогда, когда он очерчен (т. е. обведен контуром). Если бы мы не учли closePath() для очерченного треугольника, тогда только две линии были бы нарисованы, а не весь треугольник.

+ +

Дуги

+ +

Для рисования дуг и окружностей, используем методы arc() и arcTo().

+ +
+
{{domxref("CanvasRenderingContext2D.arc", "arc(x, y, radius, startAngle, endAngle, anticlockwise)")}}
+
Рисуем дугу с центром в точке (x,y) радиусом radius, начиная с угла startAngle и заканчивая в endAngle в направлении против часовой стрелки anticlockwise (по умолчанию по ходу движения часовой стрелки).
+
{{domxref("CanvasRenderingContext2D.arcTo", "arcTo(x1, y1, x2, y2, radius)")}}
+
Рисуем дугу с заданными контрольными точками и радиусом, соединяя эти точки прямой линией.
+
+ +

Рассмотрим детальнее метод arc(), который имеет пять параметров: x и y — это координаты центра окружности, в которой должна быть нарисована дуга. radius — не требует пояснений. Углы startAngle и endAngle определяют начальную и конечную точки дуги в радианах вдоль кривой окружности. Отсчет происходит от оси x. Параметр anticlockwise — логическое значение, которое, если true, то рисование дуги совершается против хода часовой стрелки; иначе рисование происходит по ходу часовой стрелки.

+ +
+

Note: Углы в функции arc() измеряют в радианах, не в градусах. Для перевода градусов в радианы вы можете использовать JavaScript-выражение: radians = (Math.PI/180)*degrees.

+
+ +

Следующий пример немного сложнее, чем мы рассматривали ранее. Здесь нарисованы 12 различных дуг с разными углами и заливками.

+ +

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

+ +

Координаты x и y  должны быть достаточно ясны. radius and startAngle — фиксированы. endAngle начинается со 180 градусов (полуокружность) в первой колонке и, увеличиваясь с шагом 90 градусов, достигает кульминации полноценной окружностью в последнем столбце.

+ +

Установка параметра clockwise определяет результат; в первой и третьей строках рисование дуг происходит по часовой стрелке, а во второй и четвертой - против часовой стрелки. Благодаря if-условию верхняя половина дуг образуется с контуром, (обводкой), а нижняя половина дуг - с заливкой.

+ +
+

Note: Этот пример требует немного большего холста (canvas), чем другие на этой странице: 150 x 200 pixels.

+
+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    for(var i=0;i<4;i++){
+      for(var j=0;j<3;j++){
+        ctx.beginPath();
+        var x = 25+j*50; // x coordinate
+        var y = 25+i*50; // y coordinate
+        var radius = 20; // Arc radius
+        var startAngle = 0; // Starting point on circle
+        var endAngle = Math.PI+(Math.PI*j)/2; // End point on circle
+        var anticlockwise = i%2==0 ? false : true; // clockwise or anticlockwise
+
+        ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
+
+        if (i>1){
+          ctx.fill();
+        } else {
+          ctx.stroke();
+        }
+      }
+    }
+  }
+}
+
+ +

{{EmbedLiveSample("Дуги", 160, 210, "https://mdn.mozillademos.org/files/204/Canvas_arc.png")}}

+ +

Безье и квадратичные кривые

+ +

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

+ +
+
{{domxref("CanvasRenderingContext2D.quadraticCurveTo", "quadraticCurveTo(cp1x, cp1y, x, y)")}}
+
Рисуется квадратичная кривая Безье с текущей позиции пера в конечную точку с координатами x и y, используя контрольную точку с координатами cp1x и cp1y.
+
{{domxref("CanvasRenderingContext2D.bezierCurveTo", "bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)")}}
+
Рисуется кубическая кривая Безье с текущей позиции пера в конечную точку с координатами x и y, используя две контрольные точки с координатами (cp1x, cp1y) и (cp2x, cp2y).
+
+ +

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

+ +

Параметры x и y в этих двух методах являются координатами конечной точки. cp1x и cp1y — координаты первой контрольной точки, а cp2x и cp2y — координаты второй контрольной точки.

+ +

Использование квадратичных или кубических кривых Безье может быть  спорным выходом, так как в отличие от приложений векторной графики типа Adobe Illustrator, мы не имеем полной видимой обратной связи с тем, что мы делаем. Этот факт делает довольно сложным процесс рисования сложных фигур. В следующем примере мы нарисуем совсем простую составную фигуру, но, если у вас есть время и ещё больше терпения, можно создать более сложные составные фигуры.

+ +

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

+ +

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

+ +

В этом примере многократно используются квадратичные кривые Безье для рисования речевой выноски.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    // Quadratric curves example
+    ctx.beginPath();
+    ctx.moveTo(75,25);
+    ctx.quadraticCurveTo(25,25,25,62.5);
+    ctx.quadraticCurveTo(25,100,50,100);
+    ctx.quadraticCurveTo(50,120,30,125);
+    ctx.quadraticCurveTo(60,120,65,100);
+    ctx.quadraticCurveTo(125,100,125,62.5);
+    ctx.quadraticCurveTo(125,25,75,25);
+    ctx.stroke();
+  }
+}
+
+ +

{{EmbedLiveSample("Квадратичные_кривые_Безье", 160, 160, "https://mdn.mozillademos.org/files/243/Canvas_quadratic.png")}}

+ +

Кубические кривые Безье

+ +

В этом примере нарисовано сердце с использованием кубических кривых Безье.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    // Cubic curves example
+    ctx.beginPath();
+    ctx.moveTo(75,40);
+    ctx.bezierCurveTo(75,37,70,25,50,25);
+    ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
+    ctx.bezierCurveTo(20,80,40,102,75,120);
+    ctx.bezierCurveTo(110,102,130,80,130,62.5);
+    ctx.bezierCurveTo(130,62.5,130,25,100,25);
+    ctx.bezierCurveTo(85,25,75,37,75,40);
+    ctx.fill();
+  }
+}
+
+ +

{{EmbedLiveSample("Cubic_Bezier_curves", 160, 160, "https://mdn.mozillademos.org/files/207/Canvas_bezier.png")}}

+ +

Прямоугольники

+ +

Все эти методы мы видели в  {{anch("Рисование прямоугольников")}}, которые рисуют прямоугольники сразу в canvas, так же есть метод rect(), который не отображает, а только добавляет контур рисования (path) заданного прямоугольника к последнему открытому контуру.

+ +
+
{{domxref("CanvasRenderingContext2D.rect", "rect(x, y, width, height)")}}
+

+ Добавляет в path прямоугольник, верхний левый угол которого указан с помощью (x, y) с вашими width и height
+
+
+ +

Когда этот метод вызван, автоматически вызывается метод moveTo() с параметрами (x, y). Другими словами, позиция курсора устанавливается в начало добавленного прямоугольника.

+ +

Создание комбинаций

+ +

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

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    roundedRect(ctx,12,12,150,150,15);
+    roundedRect(ctx,19,19,150,150,9);
+    roundedRect(ctx,53,53,49,33,10);
+    roundedRect(ctx,53,119,49,16,6);
+    roundedRect(ctx,135,53,49,33,10);
+    roundedRect(ctx,135,119,25,49,10);
+
+    ctx.beginPath();
+    ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false);
+    ctx.lineTo(31,37);
+    ctx.fill();
+
+    for(var i=0;i<8;i++){
+      ctx.fillRect(51+i*16,35,4,4);
+    }
+
+    for(i=0;i<6;i++){
+      ctx.fillRect(115,51+i*16,4,4);
+    }
+
+    for(i=0;i<8;i++){
+      ctx.fillRect(51+i*16,99,4,4);
+    }
+
+    ctx.beginPath();
+    ctx.moveTo(83,116);
+    ctx.lineTo(83,102);
+    ctx.bezierCurveTo(83,94,89,88,97,88);
+    ctx.bezierCurveTo(105,88,111,94,111,102);
+    ctx.lineTo(111,116);
+    ctx.lineTo(106.333,111.333);
+    ctx.lineTo(101.666,116);
+    ctx.lineTo(97,111.333);
+    ctx.lineTo(92.333,116);
+    ctx.lineTo(87.666,111.333);
+    ctx.lineTo(83,116);
+    ctx.fill();
+
+    ctx.fillStyle = "white";
+    ctx.beginPath();
+    ctx.moveTo(91,96);
+    ctx.bezierCurveTo(88,96,87,99,87,101);
+    ctx.bezierCurveTo(87,103,88,106,91,106);
+    ctx.bezierCurveTo(94,106,95,103,95,101);
+    ctx.bezierCurveTo(95,99,94,96,91,96);
+    ctx.moveTo(103,96);
+    ctx.bezierCurveTo(100,96,99,99,99,101);
+    ctx.bezierCurveTo(99,103,100,106,103,106);
+    ctx.bezierCurveTo(106,106,107,103,107,101);
+    ctx.bezierCurveTo(107,99,106,96,103,96);
+    ctx.fill();
+
+    ctx.fillStyle = "black";
+    ctx.beginPath();
+    ctx.arc(101,102,2,0,Math.PI*2,true);
+    ctx.fill();
+
+    ctx.beginPath();
+    ctx.arc(89,102,2,0,Math.PI*2,true);
+    ctx.fill();
+  }
+}
+
+// A utility function to draw a rectangle with rounded corners.
+
+function roundedRect(ctx,x,y,width,height,radius){
+  ctx.beginPath();
+  ctx.moveTo(x,y+radius);
+  ctx.lineTo(x,y+height-radius);
+  ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
+  ctx.lineTo(x+width-radius,y+height);
+  ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
+  ctx.lineTo(x+width,y+radius);
+  ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
+  ctx.lineTo(x+radius,y);
+  ctx.quadraticCurveTo(x,y,x,y+radius);
+  ctx.stroke();
+}
+
+ +

Конечное изображение выглядит так:

+ +

{{EmbedLiveSample("Создание_комбинаций", 160, 160, "https://mdn.mozillademos.org/files/9849/combinations.png")}}

+ +

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

+ +

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

+ +

Path2D объекты

+ +

Как мы видели в последнем примере, есть серия путей и команд для рисования объектов на вашем холсте. Чтобы упростить код и повысить производительность, объект {{domxref("Path2D")}}, доступный в последних версиях браузеров, позволяет вам кэшировать или записывать эти команды рисования. Вы можете быстро запускать свои пути.
+ Давайте посмотрим, как мы можем построить объект Path2D :

+ +
+
{{domxref("Path2D.Path2D", "Path2D()")}}
+
Конструктор Path2D() возвращает вновь созданный объект Path2D  необязательно с другим путем в качестве аргумента (создает копию) или необязательно со строкой, состоящей из данных пути SVG path .
+
+ +
new Path2D();     // пустой path объект
+new Path2D(path); // копирование из другого path
+new Path2D(d);    // path из SVG
+ +

Все  методы path , такие как  moveTo,  rect,  arc, или quadraticCurveTo,  итп, которые мы уже знаем, доступны для объектов Path2D

+ +

API Path2D также добавляет способ комбинирования путей с использованием метода addPath. Это может быть полезно, если вы хотите, например, создавать объекты из нескольких компонентов.

+ +
+
{{domxref("Path2D.addPath", "Path2D.addPath(path [, transform])")}}
+
Добавляет путь к текущему пути с необязательной матрицей преобразования.
+
+ +

Path2D пример

+ +

В этом примере мы создаем прямоугольник и круг. Оба они сохраняются как объект Path2D, поэтому они доступны для последующего использования. С новым API Path2D несколько методов были обновлены, чтобы при необходимости принять объект Path2D для использования вместо текущего пути. Здесь stroke и fill используются с аргументом пути, например, для рисования обоих объектов на холст.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    var rectangle = new Path2D();
+    rectangle.rect(10, 10, 50, 50);
+
+    var circle = new Path2D();
+    circle.moveTo(125, 35);
+    circle.arc(100, 35, 25, 0, 2 * Math.PI);
+
+    ctx.stroke(rectangle);
+    ctx.fill(circle);
+  }
+}
+
+ +

{{EmbedLiveSample("Path2D_example", 130, 110, "https://mdn.mozillademos.org/files/9851/path2d.png")}}

+ +

Использование SVG путей

+ +

Еще одна мощная функция нового Canvas Path2D API использует данные пути SVG, SVG path data, для инициализации путей на вашем холсте. Это может позволить вам передавать данные пути и повторно использовать их как в SVG, так и в холсте.

+ +

Путь перемещается в точку (M10 10), а затем горизонтально перемещается на 80 пунктов вправо (h 80), затем на 80 пунктов вниз (v 80), затем на 80 пунктов влево (h -80), а затем обратно на start (z). 
+ Этот пример можно увидеть на странице  Path2D constructor.

+ +
var p = new Path2D("M10 10 h 80 v 80 h -80 Z");
+ +
{{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_usage", "Web/API/Canvas_API/Tutorial/Applying_styles_and_colors")}}
-- cgit v1.2.3-54-g00ecf