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

Оцените материал
(0 голосов)

Проблема

Иногда перед вами встает задача показать визуальные различия между двумя изображениями, обычно в качестве иллюстрации «было — стало». Например, для того чтобы продемонстрировать в портфолио результаты обработки фотографии, показать эффект определенных процедур на веб-сайте салона красоты или проиллюстрировать видимые последствия катастрофического события в какой-то географической области. Самое распространенное решение — просто поместить два изображения рядом друг с другом. Однако при этом человеческий глаз замечает только очевидные различия и упускает мелочи. Это не страшно, если детали не так важны или же различия действительно очень значительные, но во всех остальных случаях нам требуется более удобный способ сравнения. С точки зрения удобства использования у данной проблемы есть несколько решений. Одно из наиболее распространенных — показывать оба изображения в одном и том же месте, быстро сменяя одно другим, используя для этого анимированное изображение в формате GIF или анимацию CSS. Это намного лучше, чем выводить изображения подле друг друга, но пользователю приходится потратить время на то, чтобы заметить все различия, так как ему нужно просмотреть несколько итераций, каждый раз фиксируя взгляд на новой области изображения. В некоторых вариациях пользователь всего лишь двигает указатель мыши, вместо того чтобы перетаскивать полосу. Преимущество такого подхода в том, что его проще заметить и использовать, но создаваемое мельтешение может раздражать.

Пример интерактивного виджета для сравнения изображений.
Намного более удобное для пользователя решение — так называемый слайдер сравнения изображений. Этот элемент управления содержит оба изображения, одно поверх другого, и позволяет пользователю перетаскивать разделитель, открывая одно или второе. Разумеется, такого элемента управления в HTML в действительности не существует. Его приходится имитировать средствами имеющихся элементов, и в Сети можно найти массу вариантов реализации, чаще всего требующих каркасов JavaScript и большого количества JS-кода. Существует ли более простой способ добавления на страницу подобного элемента управления? Да, причем целых два!


Решение со свойством resize в CSS

Если подумать, то слайдер сравнения изображений, по сути, состоит из изображения и элемента с изменяющимся горизонтальным размером, который постепенно открывает другое изображение. Именно здесь на помощь обычно призывается каркас JavaScript: он обеспечивает возможность изменения размера верхнего изображения по горизонтали. Однако для того, чтобы сделать размер элемента динамичным, вовсе не обязательно прибегать к помощи сценариев. В CSS User Interface Level 3 мы получили новое свойство, предназначенное специально для выполнения этойзадачи: скромное  resize !

Даже если вы никогда не слышали о таком свойстве, то наверняка видели его в действии, так как по умолчанию для него устанавливается значение  both для элементов  <textarea> , благодаря чему размер текстовых полей можно менять в обоих направлениях. Но в действительности это свойство можно устанавливать для любых элементов при условии, что значение  overflow для данного элемента не равно  visible . По умолчанию значение  resize почти для всех элементов равно  none , что запрещает изменение их размера. Помимо  both, это свойство также принимает значения  horizontal и  vertical, ограничивающие направление изменения размера.

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

Первой мыслью может быть всего лишь добавить два элемента  <img> . Однако применение  resize напрямую к  <img> дает ужасные результаты, поскольку при изменении размера изображения оно искажается. Гораздо разумнее установить это свойство для контейнера  <div> . В итоге мы получим примерно такую разметку:

HTML
<div class="image-slider">
<div>
<img src="/adamcatlace-before.jpg" alt="Before" />
</div>
<img src="/adamcatlace-after.jpg" alt="After" />
</div>


Затем применим немного простейшего CSS для позиционирования и определения размеров:
.image-slider {
position:relative;
display: inline-block;
}
.image-slider > div {
position: absolute;
top: 0; bottom: 0; left: 0;
width: 50%; /* Первоначальное значение ширины */
overflow: hidden; /* Изображение должно обрезаться */
}
.image-slider img { display: block; }


Сейчас результат выглядит как на рисунке, но элемент пока что статичный.

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

.image-slider > div {
position: absolute;
top: 0; bottom: 0; left: 0;
width: 50%;
overflow: hidden;
resize: horizontal;
}
Единственное визуальное отличие состоит в том, что теперь в правом нижнем углу изображения «до» отображается манипулятор для изменения размера, но теперь мы можем перетаскивать и менять ширину изображения сколько нашей душе угодно!

Однако поиграв с нашим виджетом, мы обнаруживаем несколько слабых сторон: ‰ изменяя ширину элемента  <div> , мы можем выходить за пределы изображений;‰ манипулятор для изменения размера не так просто заметить.

Первую проблему решить просто. Нам всего лишь нужно задать для свойства  max-width значение  100%. Второй вопрос несколько сложнее. К сожалению, не существует стандартного способа менять размер манипулятора. Некоторые механизмы визуализации поддерживают собственные псевдоэлементы (такие, как  ::-webkit-resizer ), но результаты их применения ограничены как в терминах поддержки браузерами, так и в терминах гибкости стилизации. И все же не теряйте надежды: оказывается, если наложить псевдоэлемент на манипулятор для изменения размера, то это нисколько не повредит его функциональности, даже без  pointer-events: none . Таким образом, решением, подходящим для разных браузеров, будет всего лишь… наложить на наш манипулятор еще один манипулятор. Давайте сделаем это:
.image-slider > div::before {
content: '';
position: absolute;
bottom: 0; right: 0;
width: 12px; height: 12px;
background: white;
cursor: ew-resize;
}

Обратите внимание на объявление  cursor: ew-resize — оно дополнительно намекает на возможность взаимодействия с элементом, подсказывая пользователю, что здесь находится манипулятор. Однако мы не должны надеяться на изменение внешнего вида курсора как на единственную подсказку, поскольку они заметны, только когда пользователь уже взаимодействует с элементом управления. Сейчас наш манипулятор для изменения размера выглядит как белый квадратик. Но мы можем сделать шаг вперед и изменить его стилизацию в соответствии с собственными вкусами. Например, давайте превратим его в белый треугольник, отстоящий от краев изображения на  5px. Для этого нам потребуется такой код:
padding: 5px;
background:
linear-gradient(-45deg, white 50%,
transparent 0);
background-clip: content-box;

В качестве дополнительного усовершенствования мы могли бы применить  user-select: none к обоим изображениям, чтобы ошибка при захвате манипулятора не приводила к их бессмысленному выделению. В итоге окончательная версия кода будет выглядеть так:
.image-slider {
position: relative;
display: inline-block;
}
.image-slider > div {
position: absolute;
top: 0; bottom: 0; left: 0;
width: 50%;
max-width: 100%;
overflow: hidden;
resize: horizontal;
}
.image-slider > div::before {
content: '';
position: absolute;
bottom: 0; right: 0;
width: 12px; height: 12px;
padding: 5px;
background:
linear-gradient(-45deg, white 50%,
transparent 0);
background-clip: content-box;
cursor: ew-resize;
}
.image-slider img {
display: block;
user-select: none;
}


ПОПРОБУЙТЕ САМИ!
http://play.csssecrets.io/image-slider

Решение с ползунком

Метод со свойством  resize из CSS, описанный в предыдущем разделе, отлично работает и требует совсем небольшого объема кода. Однако у него естьнесколько недостатков: ‰ им невозможно воспользоваться при работе только с клавиатуры; ‰ перетаскивание — единственный способ изменить размер верхнего изображения, что может превратиться в утомительную задачу, если изображение достаточно велико или если у пользователя нарушены моторные функции. Если бы у пользователя была также возможность щелкнуть в какой-то точке и тем самым изменить размер изображения до этой позиции, то таким виджетом пользоваться было бы намного удобнее; ‰ пользователь может изменить верхнее изображение только путем перетаскивания его правого нижнего угла, манипулятор в котором бывает сложно заметить, даже если мы определяем для него дополнительные стили, как
описано выше. Если вы не против некоторого объема сценариев, то мы можем воспользоваться элементом управления  slider (ползунок в HTML). Мы наложим его поверх наших изображений и настроим так, чтобы он мог управлять изменением размера. Это решит все три перечисленные выше проблемы. Так как мы все равно планируем использовать JS, то можем добавить еще несколько элементов посредством сценариев, поэтому начнем с самой пустой разметки, какая только возможна:

HTML
<div class="image-slider">
<img src="/adamcatlace-before.jpg" alt="Before" />
<img src="/adamcatlace-after.jpg" alt="After" />
</div>
Затем наш код JS преобразует его в следующую версию и добавит на ползунке событие, для того чтобы он также устанавливал ширину блока  div:
HTML
<div class="image-slider">
<div>
<img src="/adamcatlace-before.jpg" alt="Before" />
</div>
<img src="/adamcatlace-after.jpg" alt="After" />
<input type="range" />
</div>
Сам код JavaScript довольно прост:
JS
$$('.image-slider').forEach(function(slider) {
// Создаем дополнительный блок div и
// оборачиваем его вокруг первого изображения
var div = document.createElement('div');
var img = slider.querySelector('img');
slider.insertBefore(img, div);
div.appendChild(img);
// Создаем ползунок
var range = document.createElement('input');
range.type = 'range';
range.oninput = function() {
div.style.width = this.value + '%';
};
slider.appendChild(range);
});
CSS-код, который мы возьмем в качестве отправной точки, практически аналогичен версии из предыдущего решения. Мы только удалим несколько ненужных фрагментов:
‰ нам больше не требуется свойство  resize ;
‰ нам не требуется правило  .image-slider > div::before , так как у нас больше нет манипулятора для изменения размера;
‰ нам не требуется свойство  max-width , потому что этим значением будет управлять ползунок.
Вот как наш CSS-код выглядит после этих модификаций:

.image-slider {
position:relative;
display: inline-block;
}
.image-slider > div {
position: absolute;
top: 0; bottom: 0; left: 0;
width: 50%;
overflow: hidden
}
.image-slider img {
display: block;
user-select: none;
}

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


ползунок находится в произвольном месте под изображениями. Нам необходимо применить несколько стилей CSS, для того чтобы поместить его на изображения и совместить с ними по ширине:
.image-slider input {
position: absolute;
left: 0;
bottom: 10px;
width: 100%;
margin: 0;
}
Как видно на рисунка, результат выглядит уже вполне прилично. Существует несколько специализированных псевдоэлементов, позволяющих добавлять к ползункам желаемые стили, оформляя их по своему вкусу. Среди них  ::-moz-range-track ,  ::-ms-track, ::-webkit-slider-thumb ,  ::-moz-range-thumb и  ::-ms-thumb . Но как это часто бывает со специализированными возможностями, результаты их применения непоследовательны, хрупки и непредсказуемы, поэтому я рекомендую отказаться от их использования, если только у вас нет действительно основательных причин внедрять их.

Если еще одним нашим пожеланием является некоторая визуальная унификация ползунка с основным элементом, то нам может помочь режим смешивания и/или фильтр. Режимы смешивания  multiply ,  screen и  luminosity дают довольно хорошие результаты.
Кроме того,  filter: contrast(4) способен сделать ползунок черно-белым, а значение контраста меньше единицы способно добавить оттенков серого. Возможности бесконечны, и единственно верного выбора здесь быть не может. Вы можете даже сочетать режимы смешивания и фильтры, например так:
filter: contrast(.5);
mix-blend-mode: luminosity;

Помимо этого, мы можем увеличить область, которую пользователь использует для изменения размера, для того чтобы ему было удобнее это делать (в соответствии с законом Фиттса). Для этого уменьшим значение ширины и компенсируем различие с помощью трансформаций CSS:
width: 50%;
transform: scale(2);
transform-origin: left bottom;


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

Десерт на сегодня - видео о том, как велосипедисты лихо катаются по краю пропасти:

Прочитано 443 раз Последнее изменение Суббота, 20 мая 2017 13:25
Другие материалы в этой категории:
Понравилась запись? Подпишитесь на обновления по почте:

Нетология

TemplateMonster

geekbrains.ru/

Поиск по сайту

Google+