В этом туториале мы собираемся сделать простое веб-приложение, которое позволяет перетащить фотографию с вашего компьютера в окно браузера и применить к ней Instagram-подобные фильтры. Для этого мы будем использовать следующие JavaScript библиотеки и плагины:
- Caman.js — это мощная библиотека для работы с canvas, которая позволяет применять различные эффекты и фильтры на изображении. Библиотека поставляется с восемью предустановленными фильтрами, которые мы будем использовать в этом примере (вы можете создать больше, если хотите);
- Filereader.js — это облегченный врапер для событий перетаскивания в HTML5 (drag/drop), который значительно упрощает работу с этими событиями. Он также добавляет метод jQuery, поэтому вы можете привязывать события к определенному элементу;
- JQuery Mousewheel — я использую этот плагин для прокрутки блока с фильтрами;
- Кроме того, мы используем последнюю версию jQuery на момент написания.
Также большое спасибо Jenn и Tony Bot за их фотографию.
HTML-код
Первый шаг — написание HTML примера:
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Instagram-подобные Фильтры с использованием jQuery</title> <link href="assets/css/style.css" rel="stylesheet" /> <!-- Подключение шрифта Yanone Kaffeesatz --> <link href="https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:200,400&subset=cyrillic" rel="stylesheet"> </head> <body> <h1>Instagram <b>Filters</b></h1> <div id="photo"></div> <div id="filterContainer"> <ul id="filters"> <li> <a href="#" id="normal">Normal</a> </li> <li> <a href="#" id="vintage">Vintage</a> </li> <li> <a href="#" id="lomo">Lomo</a> </li> <li> <a href="#" id="clarity">Clarity</a> </li> <li> <a href="#" id="sinCity">Sin City</a> </li> <!-- Здесь указываются все фильтры --> </ul> </div> <!-- Библиотеки --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script src="assets/js/filereader.min.js"></script> <script src="assets/js/caman.full.js"></script> <script src="assets/js/jquery.mousewheel.min.js"></script> <script src="assets/js/script.js"></script> </body> </html> |
В дополнение к библиотекам, упомянутым вначале, мы также подключаем файл script.js, в котором содержится код, который мы будем писать ниже. В хедере подключаем шрифт Yanone Kaffeesatz из Google Web Fonts.
JavaScript / jQuery
Чтобы приложение работало, нам нужно будет сделать следующее:
- Принять изображение при перетаскивании (drag and drop);
- Создать новый элемент canvas (оригинальный) с максимальным размером 500×500 пикселей (опционально) и сохранить его в памяти;
- Прослушивать клики по фильтрам. Когда выбран один из фильтров:
- Создать клон оригинального canvas;
- Удалить все элементы canvas, находящиеся на странице;
- Добавить клон в блок
#photo
; - Если выбранный фильтр отличается от «Текущего», вызвать библиотеку Caman.js. В противном случае ничего не делать;
- Отметить выбранный фильтр классом
active
.
- Переключить фильтр на «Текущий».
Теперь, когда мы знаем, что нужно сделать, давайте начнем кодить!
assets/js/script.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
$(function() { var maxWidth = 500, maxHeight = 500, photo = $('#photo'), originalCanvas = null, filters = $('#filters li a'), filterContainer = $('#filterContainer'); // Используем плагин fileReader.js для прослушивания на // перемещение файла (drag and drop) на блок photo: photo.fileReaderJS({ on:{ load: function(e, file){ // Переместили изображение в div. var img = $('<img>').appendTo(photo), imgWidth, newWidth, imgHeight, newHeight, ratio; // Удаление canvas элемента со страницы // при предыдущем перемещении изображения. photo.find('canvas').remove(); filters.removeClass('active'); // Когда фото успешно загружено, // определяем ширину/высоту: img.load(function() { imgWidth = this.width; imgHeight = this.height; // Расчет размеров изображения, чтобы оно влезло // в указанные нами maxWidth x maxHeight if (imgWidth >= maxWidth || imgHeight >= maxHeight) { // Если изображение больше, // ресайзим его до габаритов 500x500! if (imgWidth > imgHeight) { // широкое ratio = imgWidth / maxWidth; newWidth = maxWidth; newHeight = imgHeight / ratio; } else { // высокое или квадратное ratio = imgHeight / maxHeight; newHeight = maxHeight; newWidth = imgWidth / ratio; } } else { newHeight = imgHeight; newWidth = imgWidth; } // Создаем оригинальный canvas. originalCanvas = $('<canvas>'); var originalContext = originalCanvas[0].getContext('2d'); // Устанавливаем атрибуты для центрирования canvas originalCanvas.attr({ width: newWidth, height: newHeight }).css({ marginTop: -newHeight/2, marginLeft: -newWidth/2 }); // Рисуем перемещенное на canvas изображение // с новыми размерами originalContext.drawImage(this, 0, 0, newWidth, newHeight); // Это нам больше не нужно img.remove(); filterContainer.fadeIn(); // Запускаем "текущий" фильтр по умолчанию filters.first().click(); }); // Устанавливаем источник изображения, который // запускает событие load: img.attr('src', e.target.result); }, beforestart: function(file){ // Принимаем только изображения, // возвращает false при отказе. return /^image/.test(file.type); } } }); // Прослушивание кликов по фильтрам filters.click(function(e){ e.preventDefault(); var f = $(this); if(f.is('.active')){ // Применяет фильтр только один раз return false; } filters.removeClass('active'); f.addClass('active'); // Клонирование canvas var clone = originalCanvas.clone(); // Клонирование изображения сохраненного в canvas clone[0].getContext('2d').drawImage(originalCanvas[0],0,0); // Добавление клона на страницу и запуск // библиотеки Caman.js photo.html(clone); var effect = $.trim(f[0].id); Caman(clone[0], function () { // Если этот эффект есть, использовать его: if( effect in this){ this[effect](); this.render(); } }); }); // Используем плагин mousewheel для скролла filterContainer.find('ul').on('mousewheel',function(e, delta){ this.scrollLeft -= (delta * 50); e.preventDefault(); }); }); |
Этот пример работает во всех браузерах, поддерживающих перетаскивание (drag/drop) файлов. Некоторые из фильтров являются вычислительно затратными, поэтому возможны небольшие запаздываниями при выводе результата в браузере. Я ограничил максимальную ширину/высоту изображения размером 500 пикселей, чтобы немного ускорить работу, но вы можете изменить эти значения по своему усмотрению.
Использование атрибута загрузки HTML5
Атрибут загрузки HTML5 указывает браузеру, что вместо отображения ссылки он должен загрузить его как файл. Более того, значение этого атрибута — это имя файла, которое будет указано при сохранении. На данный момент 78% устройств поддерживают этот атрибут. Чтобы использовать его, мы должны создать ссылку для загрузки изображения и добавить к нему атрибут:
index.html
1 2 3 |
<div id="photo"> <a href="#" class="downloadImage" target="_blank" download="photo.png">Download Image</a> </div> |
Атрибут href
будет заменен на DataURL изображения, которое легко получить из canvas. Я поместил ссылку в div #photo
, чтобы она шла сразу после изображения.
Затем стилизуем нашу ссылку:
styles.css
1 2 3 4 5 6 7 8 9 10 11 |
.downloadImage{ position: absolute; display: none; text-indent: -999px; top: 100px; right: -260px; overflow: hidden; width: 206px; height: 153px; background: url('../img/download_image.jpg'); } |
Оны должена выглядеть так:
И последнее, что нам нужно сделать, включить две функции в основной JavaScript файл:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var downloadImage = $('a.downloadImage'); function showDownload(canvas){ downloadImage.off('click').click(function(){ // При клике по ссылке загрузки изображения, // берем DataURL изображения и вставляем в href: var url = canvas.toDataURL("image/png;base64;"); downloadImage.attr('href', url); }).fadeIn(); } function hideDownload(){ downloadImage.fadeOut(); } |
Эти функции показывают и скрывают кнопку загрузки соответственно. Функция showDownload срабатывает при клике. Когда это происходит, она преобразует элемент canvas
в строку dataURL
и устанавливает атрибут href
. Поскольку у нас уже есть атрибут download
, это загрузит содержимое canvas как photo.png
. Я вызываю эти функции как callback функции в библиотеке Caman.js:
script.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Caman(clone[0], function () { // Если этот эффект есть, использовать его: if( effect in this){ this[effect](); this.render(); // Показать кнопку загрузки showDownload(clone[0]); } else{ hideDownload(); } }); |
Код показывает кнопку только тогда, когда фильтр отличается от «Текущего».
С этим наше приложение готово! Я надеюсь, что вы найдете его полезным.
Не совсем точный перевод статей на: