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

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

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

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

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

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

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

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

Эти источники совместно именуемые по типу CanvasImageSource.

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

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

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

  •  document.images коллекция
  • The document.getElementsByTagName() метод
  • Если вы знаете id конкретного изображения, который вы хотите использовать, вы можете использовать document.getElementById (), чтобы получить это конкретное изображение

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

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

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

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

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

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

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

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

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

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

    return document.getElementById('myvideo');
  }
}

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

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

Как только мы получили ссылку на источник объекта изображения, мы можем использовать метод drawImage() для включения его в  canvas. Как мы увидим далее, метод 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';
}

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

ScreenshotLive sample

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

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

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

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

В этом примере, мы будем использовать изображение в качестве обоев и повторим его  в  canvas несколько раз. Это может быть сделано просто через цикл, располагая измененные изображения на разных позициях. В коде внизу, первый цикл проходит по рядам. Второй цикл проходит по колонкам. Изображение уменьшено на треть от реального размера, которое было  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 выглядит так:

ScreenshotLive sample

Нарезка

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

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

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

ScreenshotLive sample

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

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

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

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

Код ниже должен говорить сам за себя. Мы проходим циклом через document.images контейнер и соответственно добавляем новые элементы  canvas. Возможно следует упомянуть для тех, кто не слишком хорошо знаком с DOM, что для этого используется 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
      canvas = document.createElement('canvas');
      canvas.setAttribute('width',132);
      canvas.setAttribute('height',150);

      // Вставляет перед изображением
      document.images[i].parentNode.insertBefore(canvas,document.images[i]);

      ctx = canvas.getContext('2d');

      // Рисует изображение в canvas
      ctx.drawImage(document.images[i],15,20);

      // Добавляет рамку
      ctx.drawImage(document.getElementById('frame'),0,0);
    }
  }
}

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

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

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

Метки документа и участники

Метки: 
 Внесли вклад в эту страницу: Theana27, Feuway, 4esnog, MaxManchak, Taova, MuradAz
 Обновлялась последний раз: Theana27,