HTML5 web приложение с фильтрами как в Instagram. Часть 2.
Посмотреть демо приложения Вы можете на моём аккаунте codepen: http://codepen.io/nikitakiselev/full/kXyBAP/.
HTML5 web приложение с фильтрами как в Instagram. Часть 1.
Для получения исходников проекта из первой статьи, необходимо склонировать репозиторий и перейти к первому коммиту:
123
git clone https://github.com/mblog-repository/html5-insta-filters.git
cd html5-insta-filters
git checkout 05002ccda2eecbb24415d9ab323a107f331e2450
Теперь у Вас есть каркас приложения, для второй части статьи.
- Техническое задание
- Введение
- Подготовка инструментов
- Добавление проекта на GitHub
- Написание HTML каркаса
- Написание Javascript кода
- Написание CSS кода
- Заключение
Файл index.html был создан в предыдущей статье, поэтому открываем его и напишем там простой HTML каркас:
index.html
12345678910
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>HTML5 web приложение с фильтрами как в Instagram</title>
</head>
<body>
</body>
</html>
Т.к. html код довольно объёмный и не представляет особого интереса, то здесь я опишу только ключевые моменты. Полностью посмотреть как выглядит файл index.html вы сможете в репозитории.
Следующим шагом будет написание html кода для приложения:
123456789101112131415161718192021222324252627
<div id="photo-box" class="photo-box">
<div class="label">
<img src="https://ucarecdn.com/c7aeb97e-a73f-40bb-8966-45d692d6dde1/upload.png" alt="Upload icon" width="100" height="100"/>
<div>Нажмите или перетащите сюда изображение...</div>
</div>
<div id="loader" class="loader"></div>
</div>
<div class="download">
<a href="#" id="download-link" class="download-link">
Скачать изображение
</a>
</div>
<input type="file" id="file-picker" class="file-picker"/>
<div id="filters" class="filters">
<div class="preset preset_original" data-preset="original">
<span class="preset-name">Original</span>
</div>
<div class="preset preset_vintage" data-preset="vintage">
<span class="preset-name">Vintage</span>
</div>
<div class="preset preset_lomo" data-preset="lomo">
<span class="preset-name">Lomo</span>
</div>
</div>
Здесь в блоке div#photo-box будет находиться загружаемое пользователем изображение. Элемент <canvas>, на котором будет отрисоваваться выбранное изображение, создаётся динамически, поэтому сейчас его нет. Сейчас внутри блока мы видит блок .label, который выводит текст "Нажмите или перетащите сюда изображение..." и иконку. Блок #loader служит для индикации работы над изображением, это будет полезно на слабых компьютерах, когда эффект применяется к изображению не мгновенно. Без этого можно обойтить, но мы сделаем приложение немного "user friendly".
Далее в блоке #download находится ссылка для скачивания текущего изображения, отрисованного на <canvas>. Ссылка по умолчанию скрыта с помощью CSS стилей, и появляется когда применяется фильтр к изображению.
Элемент #file-picker так же скрыт с помощью CSS. Это стандартный элемент выбора файла. В техническом задании написано, что необохдимо предусмотреть возможность загрузки изображения с помощью стандартного диаголового окна выбора файла. Элемент скрыт, но при клике на область #photo-box, будет срабатывать событие click на #file-picker, открывая тем самым диплоговое окно выбора файла. Всё просто.
Ну а дальше идёт блок #filters, в котором выведены эффекты, которые можно применить к изображению. Этот блок появляется только тогда, когда пользователь выбрал изображение. Я не стал приводить в статье полный список всех доступных фильтров - их много и весь список вы найдёте в репозитории проекта на github. У каждого фильтра есть аттрибут data-preset="original", в котором прописано название фильтра. Это название мы и будем считывать по клику на фильтр, чтобы применить его к изображению.
На этом обзор основных моментов HTML структуры приложения закончен.
++++
primer-raboty-prilozheniya.jpgНаконец то мы подошли к самому интересному - написанию javascript.
Продумаем логику работы приложения:
- Получить изображение путём перетаскивания его на определённую область или выбором с помощью системного диалогового окна.
- Создать новый элемент
canvas размером 500x500 px поместить его в DOM (Document Object Model — «объектная модель документа»). - Ожидать клика на какой нибудь фильтр.
- Применить выбранный эффект с помощью библиотеки Caman.js
- Если выбранный фильтр "original", вернуть начальное состояние изображения.
- Добавить к выбранному фильтру класс
.active. - При выборе другого изображения, удалить старый
<canvas> и начать всё с первого пункта.
Теперь, когда мы знаем, что нужно делать, давайте же сделаем то, ради чего мы сдесь собрались - запрограммируем это. Писать весь код будем в файле src/js/app.js, но перед этим необходимо запустить сборщик проекта - gulp watch.
Для начала напишем логику выбора изображения по клику на область.
src/js/app.js
1234567
let $photoBox = $('#photo-box');
$filePicker.on('change', event => {
if (event.target.files.length) {
uploadFile(event.target.files[0]);
}
});
Здесь просто отлавливаем события клика на #photo-box, и если выбраны файлы, то передаём первый файл из массива в функцию uploadFile. Функция uploadFile не сложная, в ней мы выполняем провернку mime типов изображения и
12345678910111213141516171819
let $loader = $('#loader');
let image = new Image();
let uploadedFileName = 'image';
function uploadFile(file) {
if (file.type.match('image/jpg') || file.type.match('image/png')) {
$loader.show();
image.src = URL.createObjectURL(file);
uploadedFileName = file.name.split('.').shift();
} else {
alert('Поддерживаются только файлы jpg и png');
}
}
Теперь остаётся отловить событие загрузки изображения image, создать элемент canvas и отрисовать на нём это изображение. Здесь немного сложнее, но мы рассмотрим этот код, плюс я оставил подробные комментарии.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
let maxWidth = $photoBox.width(),
maxHeight = $photoBox.height(),
imageWidth = maxWidth,
imageHeight = maxHeight;
image.onload = function() {
let width = this.width,
height = this.height;
if (width >= maxWidth || height >= maxHeight) {
if (width > height) {
let ratio = width / maxWidth;
imageWidth = maxWidth;
imageHeight = height / ratio;
} else {
let ratio = height / maxHeight;
imageHeight = maxHeight;
imageWidth = width / ratio;
}
} else {
imageWidth = width;
imageHeight = height;
}
$label.hide();
$filters.addClass('active');
$filters.find('.preset')
.first()
.trigger('click');
let $oldCanvas = $photoBox.find('canvas');
let $photo = $('<canvas>').addClass('photo').attr({
id: 'photo',
width: imageWidth,
height: imageHeight
}).css({
width: $oldCanvas.width() || '0px',
heigth: $oldCanvas.height() || '0px'
});
$oldCanvas.remove();
$photoBox.append($photo);
let ctx = $photo[0].getContext('2d');
ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
$photo.css({
width: imageWidth + 'px',
height: imageHeight + 'px'
});
$loader.hide();
}
Верстка проекта примитивная. Хочу обратить внимание на несколько моментов.
Изменение стилей полосы прокрутки поддерживают только браузеры, основанные на webkit. Можно было подключить js плагин, который сделает свою прокрутку с возможностью её кастомизации. Такой вариант будет работать во всех браузерах, но я решил не перегружать проект лишними плагинами, поэтому воспользовался кастомизацией только для webkit:
12345678910111213141516
&::-webkit-scrollbar {
height: 4px;
background-color: rgba(100,100,100,0.2);
border-radius: 2px;
}
&::-webkit-scrollbar-thumb {
border-radius: 1px;
background: #fff;
box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
cursor: pointer;
}
&::-webkit-scrollbar-thumb:window-inactive {
background: #aaa;
}
1
### Вывод пресетов
Для визуализации фильтров используются заранее подготовленные изображения. Т.к. изображений много, то для экономии сокращения времени загрузки страницы был сгенерирован спрайт, содержащий эти изображения.
++++
presets.pngВыводятся эти изображения с помощью css:
1234
.preset.preset_clarity { background-position: 0 0; }
.preset.preset_concentrate { background-position: -100px 0; }
.preset.preset_crossProcess { background-position: -200px 0; }
.preset.preset_glowingSun { background-position: -300px 0; }
На этом описание разработки проекта закончено. Осталось добавить изменения в репозиторий.
123
git add .
git commit -m 'Complete project'
git push
Ссылка на репозиторий проекта: https://github.com/mblog-repository/html5-insta-filters
Если у Вас есть вопросы по этим статьям, то задавайте их в комментариях. Если Вы нашли неточности в статье или в коде, так же пишите в комментариях, я буду их исправлять.