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 кода
- Заключение
Написание HTML каркаса
Файл 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 кода
Наконец то мы подошли к самому интересному - написанию 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();
}
Написание CSS кода
Верстка проекта примитивная. Хочу обратить внимание на несколько моментов.
Изменение стиля полосы прокрутки
Изменение стилей полосы прокрутки поддерживают только браузеры, основанные на 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
Если у Вас есть вопросы по этим статьям, то задавайте их в комментариях. Если Вы нашли неточности в статье или в коде, так же пишите в комментариях, я буду их исправлять.