Статьи

Холст с нуля: пиксельная манипуляция

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

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


Вы собираетесь использовать тот же шаблон HTML из предыдущих статей, поэтому откройте ваш любимый редактор и скопируйте следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
 
<html>
    <head>
        <title>Canvas from scratch</title>
        <meta charset=»utf-8″>
 
        <script src=»http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js»></script>
 
        <script>
            $(document).ready(function() {
                var canvas = document.getElementById(«myCanvas»);
                var ctx = canvas.getContext(«2d»);
            });
        </script>
    </head>
 
    <body>
        <canvas id=»myCanvas» width=»500″ height=»500″>
            <!— Insert fallback content here —>
        </canvas>
    </body>
</html>

Это не более чем обычная HTML-страница с элементом canvas и некоторым JavaScript, который запускается после загрузки DOM. Ничего сумасшедшего.


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

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

Одна из моих фотографий с Flickr

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

Загрузка изображения на холст требует двух шагов. Во-первых, это загрузить изображение в HTML-элемент image , что можно сделать с помощью HTML или путем создания нового элемента DOM непосредственно в JavaScript. В этом примере вы собираетесь создать новый элемент DOM — он очень прост:

Все, что вы делаете здесь — это создаете новый элемент Image DOM и присваиваете его переменной. Затем вы используете эту переменную для загрузки вашего изображения, установив для атрибута src изображения правильный путь. Стоит отметить, что вы можете загрузить удаленное изображение, используя эту технику, но это поднимает несколько вопросов для нас в дальнейшем, поэтому мы пока будем придерживаться локально сохраненного изображения. Последний шаг — прослушивание события load которое будет запущено, как только изображение завершит загрузку и станет доступным для использования.

Как только изображение загрузится, вы можете поместить его на холст за один простой шаг. Все, что вам нужно сделать, это передать только что созданную переменную image в вызов метода drawImage 2-го контекста рендеринга. Поместите его в событие загрузки image , вот так:

В этом случае метод drawImage принимает три аргумента; элемент изображения, а также значения координат x и y для размещения изображения на холсте. Это нарисует изображение в полном размере (500 пикселей для этого изображения) и в указанной позиции:

Размещение изображения

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

Это позволит нарисовать изображение в половине исходного размера (250 пикселей для этого изображения):

Размещение и изменение размера изображения

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

Для этого нужно взять квадрат в 200 пикселей в левом верхнем углу изображения и нарисовать его на холсте в квадрате 500 пикселей:

Размещение только части изображения

В псевдокоде полные девять аргументов drawImage могут быть описаны следующим образом (s означает источник, а d означает назначение):

И результат представлен на следующей иллюстрации:

Принимая ничья до крайности

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


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

Если вы хотите получить доступ к пикселям с помощью canvas, вам следует помнить об ограничениях безопасности, которые на них распространяются. Эти ограничения позволяют вам получать доступ только к изображениям, загруженным в тот же домен, что и JavaScript. Это предотвращает доступ к изображению с удаленного сервера и последующий анализ его пикселей, хотя есть способ обойти его , вроде. К сожалению, не все браузеры обрабатывают JavaScript и изображения, запускаемые локально из файловой системы (т. Е. Без имени домена), как в одном домене, так что вы можете получить ошибки безопасности. Чтобы обойти это, вам нужно либо запустить оставшуюся часть этого учебника в локальной среде разработки (например, MAMP, WAMP или XAMPP) или на удаленном веб-сервере и получить доступ к файлам, используя доменное имя (например, example.com).

С этим из пути, давайте взломать и получить нам несколько пикселей!

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

Чтобы получить доступ к пикселям на холсте, вам нужно вызвать метод getImageData контекста рендеринга 2d, например:

Этот метод принимает четыре аргумента, которые описывают прямоугольную область холста, из которой вы хотите получить данные пикселей; начало координат x и y , за которыми следуют ширина и высота . Он возвращает CanvasPixelArray который содержит все значения цвета для пикселей в пределах определенной области. Первое, что следует отметить с помощью CanvasPixelArray состоит в том, что каждый пиксель имеет четыре значения цвета, поэтому индекс первого значения цвета для каждого пикселя в массиве будет кратен 4 (0 для первого значения первого пикселя, 4 для первое значение второго и т. д.):

Значения индекса в CanvasPixelArray

Что интересно в этом массиве (или раздражает, в зависимости от того, как вы на него смотрите), так это то, что не существует понятия (x, y) координатной позиции, что означает, что получить значения цвета для определенного пикселя немного сложнее, чем получить доступ к размерный массив (например, используя pixelArray [0] [3] для доступа к пикселю в (1, 4)). Вместо этого вам нужно использовать небольшую формулу, которую на самом деле очень легко понять, как только она будет правильно объяснена:

Доступ к определенному пикселю из CanvasPixelArray

Можете ли вы решить, что здесь происходит? Давайте разберем его и представим, что мы хотим получить значения цвета пикселя для самого внутреннего пикселя в сетке пикселей 3×3 — пикселя в (2, 2).

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

Первая часть проста, вы уже знаете, что на пиксель есть четыре значения цвета, и вы уже знаете ширину сетки (3 пикселя). Чтобы вычислить индекс пикселя в строке y (2), вы передаете эти значения через первую часть формулы, которая будет выглядеть так:

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

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

Вы можете решить все это, если хотите, но ответ 4, который при добавлении к предыдущему значению дает индекс 16. Круто, эй?

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

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

Код для этого примера довольно прост, поэтому давайте атакуем все сразу:

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

В обработчике кликов вы хотите определить пиксель, который щелкнул мышкой на холсте. Для этого вам сначала нужно рассчитать смещение в пикселях от верхней левой позиции холста от верхнего левого края окна браузера, для этого можно использовать метод offset jQuery. Затем вы можете вывести пиксель, на который щелкнули по холсту, вычтя смещение из положения мыши события щелчка ( pageX и pageY ). Вы должны определенно потратить некоторое время на чтение события JavaScript, если вы хотите понять это дальше.

Следующие четыре строки захватывают CanvasPixelArray для холста ( getImageData ), сохраняют его в переменной, находят индекс значения красного цвета для выбранного пикселя, вычисляя его по формуле, которую вы видели ранее, а затем сохраняют значения цвета пикселя. в виде строки CSS rgba . Наконец, последний шаг — установить цвет фона элемента body на цвет пиксела, по которому щелкнули.

Создание основного палитры цветов

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

Это был долгий путь, но теперь вы можете быстро и легко получить значения цвета любого пикселя на холсте. Я говорил вам, что вы также можете изменить цветовые значения пикселей на холсте? Я не сделал? К сожалению! Давайте теперь посмотрим на это, оно чертовски круто.


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

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

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

Здесь нет ничего сумасшедшего; вы просто умножаете номер пикселя ( i ) на 4, чтобы получить индекс значения красного цвета для этого пикселя в CanvasPixelArray . Добавив 1 или 2 к этому числу, вы можете получить и изменить значения зеленого и синего цветов соответственно.

Наконец, все, что вам нужно сделать сейчас, это очистить холст (чтобы избавиться от обычного изображения), а затем использовать метод putImageData 2-го контекста рендеринга, чтобы нарисовать сохраненный CanvasPixelArray на холст:

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

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

В canvas есть много всего, что нужно для манипулирования пикселями, но я надеюсь, что в этой статье вы уже достаточно разбирались, чтобы ваши соки текли. Я рекомендую вам изучить эту область дальше и посмотреть, что еще можно сделать с пикселями. Почему? Потому что все методы, которые вы использовали для манипулирования пикселями, могут использоваться как для видео HTML5, так и для изображений. Теперь это круто!

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