Как правильно уменьшать и обрезать изображение (фото). Как правильно уменьшать и обрезать изображение (фото) Где это может пригодиться

Практика реализации процесса адаптации изображений все еще находится в стадии развития. Существует большое количество идей и предложений как следует обрабатывать картинки.

В данном уроке мы рассмотрим небольшую очаровательную библиотеку, которая позволяет не только автоматически изменять размер изображений при изменении параметров окна просмотра, но также обрезать картинки, принимая во внимание заданную точку фокуса. К тому же, все действия осуществляются на чистом CSS.

Встречайте Focal Point

Focal Point - проект на GitHub и библиотека CSS? созданная Адамом Бредлеем (Adam Bradley). В соответствии с концепцией адаптивности изображения должны изменять размер и положение для достижения оптимальных пропорций для текущего окна просмотра. Focal Point продвигает данную идею дальше и не только изменяет размер изображений, но и обрезает их.

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

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

Как это работает?

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

Когда вы вставляете изображение в веб страницу с помощью Focal Point, оно автоматически разделяется на невидимую сетку с 12×12 ячеек. Нет разницы, какого размера картинка, сетка адаптируется под него.

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

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

Код

Теперь мы можем указать точку фокуса и пришло время разобраться в коде реализации. Для начала загрузите проект с GitHub и привяжите CSS файл к вашему документу HTML.

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

Как вы можете заметить, внешний элемент div имеет класс “focal-point ”, а во внутреннем размещено наше изображение без каких либо классов.

Переводим единицы сетки в классы

Теперь надо указать Focal Point на место, где находится фокус изображения. Наша точка фокуса смещена на три единицы вправо и три единицы вверх. Поэтому указываем для внешнего элемента div классы “right-3 ″ и “up-3 ″.

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

Обратите внимание, что изображение справа не только меньше, но и обрезано вокруг важной части. Все выполняется с помощью CSS!

Структура страницы

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

Определим элемент div с классом “column ”:

Добавим заголовок и параграф для имитации содержания страницы:

Focal Point

Lorem ipsum....

Затем вставим изображение, как было показано в предыдущем примере (с двумя элементами div и классами для точки фокуса):

Focal Point

Lorem ipsum....

И для завершения примера скопируем код с использованием другого изображения и другой точки фокуса.

Focal Point

Lorem ipsum...

Focal Point

Lorem ipsum...

Для второго изображения точка фокуса находится в другом месте:

CSS

Определим стили для нашей страницы. Адаптация изображений и работа с точкой фокуса находятся вне зоны нашей деятельности. Все что нужно сделать - определить внешний вид элементов. Для примера страница делится на две колонки и задаются стили для текста.

* { margin: 0; padding: 0; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; } .column { float: left; overflow: auto; padding: 20px; width: 50%; } h1 { text-transform: uppercase; font: bold 45px/1.5 Helvetica, Verdana, sans-serif; } p { margin-bottom: 20px; color: #888; font: 14px/1.5 Helvetica, Verdana, sans-serif; } @media all and (max-width: 767px) { p { font-size: 12px; } h1 { font-size: 35px; } } @media all and (max-width: 550px) { h1 { font-size: 23px; } }

Смотрим в действии

Теперь можно посмотреть как работает демонстрация. Если страницы выводится в большом окне просмотра (например, на экрана монитора настольного компьютера), то изображения будут выводиться в полном объеме:

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

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

Совместимость с браузерами

Библиотека работает во всех новых браузерах. В IE8 изображения изменяют размер, но не обрезаются. Но на текущий момент 99.99% сайтов не имеют подобного механизма адаптации изображения с автоматической обрезкой, так что реакция IE8 не является критичной.

Что находится внутри?

Теперь рассмотрим, как работает библиотека.

Первая часть кода является базовым решением CSS для адаптивных изображений.

Focal-point { width: 100%; height: auto; overflow: hidden; } .focal-point img { width: 100%; max-width: 100%; height: auto; -ms-interpolation-mode: bicubic; } .focal-point div { position: relative; max-width: none; height: auto; }

Следующий код немного сложнее. Сначала медиа запрос реализуется в точке 767px. Затем, для обрезки изображения используются отрицательные значения полей для каждого из доступных классов. В тексте урока мы приводим только классы “up-3 ″ и “right-3 ″, которые использовались в демонстрации.

@media all and (max-width: 767px) { /* 4x3 Landscape Shape (Default) */ .focal-point div { margin: -3em -4em; } /* Landscape up (Total 6em) */ .up-3 div { margin-top: -1.5em; margin-bottom: -4.5em; } .right-3 div { margin-left: -6em; margin-right: -2em; } }

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

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

Раньше я при публикации записи на WordPress подготавливал несколько вариантов одной и той же картинки с разными размерами. Большие (оригинального размера) использовались для показа в галерее, средние в теле самой записи, а маленькие в качестве миниатюр (thumbnail).

Со временем, мне надоело это делать из-за временных затрат и ошибок, которые нет-нет, да возникали при ручном преобразовании. Кроме того у меня возникали сложности при смене дизайна сайта, когда требовались другие размеры для изображений. Поэтому я стал преобразовывать картинки «на лету» с помощью WordPress плагина Kama Thumbnail . Спасибо автору за этот отличный плагин!

В этой же статье я расскажу как с помощью только «голого» CSS без постороннего PHP или JavaScript кода изменить размеры выводимого на экран изображения. Забегая вперед скажу, что само оригинальное изображение не меняется, равно как и не создается куча мелких файлов с другими соотношениями сторон картинки, а все действия влияют лишь на то, что будет отображаться у посетителя сайта на экране. Ну это как надеть очки с красными линзами, когда несмотря на то, что будете видеть вы — небо по прежнему останется голубым, а трава зеленой.

Css-свойство object-fit

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

Здесь проще всего показать все на примерах. Допустим у нас есть две картинки размерами 200х300 пикселей и 300х200 пикселей, а для миниатюр к постам мы хотим использовать изображение размером 200х200 пикселей. Разумно, чтобы первичные изображения полностью заполняли миниатюру с сохранением пропорций, а лишние части (сверху/снизу или слева/справа) отсекались.

Чтобы реализовать задуманное нужно использовать ключевое значение object-fit: cover; , при его использовании лишнее содержимое изображения обрезается, а итоговая картинка выравнивается по центру с сохранением пропорций таким образом, чтобы полностью заполнить область контейнера.

Как сжать и обрезать изображение на CSS

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

Img.object-fit-cover { width: 200px; height: 200px; object-fit: cover; }

Сам вывод изображения может быть таким:

Другие значения object-fit для преобразования изображений

CSS свойство object-fit не ограничено приведенным выше примером, рекомендую ознакомиться со всеми его возможностями в списке использованных источников внизу статьи.

Достоинства и недостатки преобразования размеров изображений средствами одного CSS

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

К минусам стоит отнести то, что все преобразования производятся на стороне пользователя его браузером. И тут, в теории, можно получить несколько разный результат в зависимости от движка интернет обозревателя и операционной системы посетителя сайта.

Кроме того, если для миниатюры 100х100 пикселей использовать картинку формата FullHD 1920×1080, то сначала она полностью скачается на компьютер пользователя, а лишь затем браузер приведет ее к формату 100х100. Как вы понимаете размер таких изображений (1920×1080 и 100х100) может различаться в 10 раз, и это может замедлить работу сайта на слабых компьютерах и медленном интернете (например на телефонах и планшетах в сетях 2G/3G)!

На мой взгляд плюсы перевешивают минусы.

Благодарности

При написании статьи были использованы следующие источники.

Описание

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

Синтаксис

clip: rect(Y1, X1, Y2, X2) | auto | inherit

Значения

В качестве значений используется расстояние от края элемента до области вырезки, которое задается в единицах CSS — пикселы (px), em и др. Если край области нужно оставить без изменений, следует установить auto , положение остальных значений показано на рис. 1.

Рис. 1. Значения свойства clip

HTML5 CSS2.1 IE Cr Op Sa Fx

clip

Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat. Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.

Результат данного примера показан на рис. 2.

Рис. 2. Применение clip в браузере Safari

Объектная модель

document.getElementById("elementID ").style.clip

Браузеры

Internet Explorer до версии 7.0 включительно работает с другой формой записи, при которой значения координат разделяются между собой пробелом, а не запятой - clip : rect(40px auto auto 40px) . Также Internet Explorer до версии 7.0 включительно не поддерживает значение inherit .

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

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

Способ 1. Использование отрицательных полей

Изображение необходимо поместить в родительский элемент, в нашем случае - div. Родительский элемент должен быть обтекаемым элементом (или с заданной шириной). Метод не будет работать на блочных элементах или элементах во всю ширину.

Установим отрицательные поля для всех четырех сторон: top (сверху), right (справа), bottom (снизу) и left (слева). Отрицательные поля определяют, насколько изображение, находящееся в родительском элементе обрезано в каждом направлении. Затем установим свойство родительского элемента overflow (перекрывание) на hidden (скрыть), чтобы скрыть поля, которые находятся за областью вырезанного изображения.

    <div class = "crop" >

    Crop

    float : left ;

    overflow : hidden ;

    Crop img

    margin : -70px -50px -160px -175px ;

Способ 2. Использование абсолютного позиционирования

По этому методу задаем ширину и высоту родительского эдемента, свойство position (позиционирование) устанавливаем relative (относительным). Ширина и высота определяют размеры отображаемого поля. Для изображения внутри родительского элемента свойство позиционирования задаем absolute (абсолютным). Затем с помощью свойств top (сверху) и left (слева) задаем, какую часть изображения показывать.

    <div class = "crop" >

    Crop

    float : left ;

    overflow : hidden ;

    position : relative ;

    width : 270px ;

    height : 260px ;

    Crop img

    position : absolute ;

    top : -70px ;

    left : -175px ;

Способ 3. Использование свойства сlip

Этот способ самый простой, так как свойство clip определяет часть элемента, которую надо показать. Но этот метод имеет два недостатка.

Во-первых, обрезанный элемент должен позиционироваться абсолютно. Поэтому нам придется добавить дополнительный элемент, вычислить размер видимой области изображения, добавить этот размер и задать свойство float родителю.

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

    <div class = "crop" >

    Crop

    float : left ;

    position : relative ;

Узнайте, как изменять размер и обрезать изображения с помощью JavaScript и элемента HTML5 Canvas , используя при этом инструменты управления, которые вы могли видеть в приложениях для редактирования фотографий:

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

В реальной среде веб-сайт или приложение могут использовать эту технику для изменения размера и создания фотографии профиля перед загрузкой.

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

Для этого мы создадим элемент HTML5 и выведем изображение на холсте в определенном размере, а затем извлечем новые данные изображения с холста в виде данных URI .

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

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

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

Окончательный результат вы можете увидеть в этой демо-версии , или вы можете скачать ZIP-архив .

Что ж, теперь давайте приступим!

Разметка

В нашей демо-версии мы начнем с существующего изображения:

Вот и все! Это весь HTML -код, который нам потребуется для этой демонстрации.

CSS

CSS также самый минимальный. Во-первых, определим стили для контейнера изменения размера и изображения:

Resize-container { position: relative; display: inline-block; cursor: move; margin: 0 auto; } .resize-container img { display: block } .resize-container:hover img, .resize-container:active img { outline: 2px dashed rgba(222,60,80,.9); }

Resize-handle-ne, .resize-handle-ne, .resize-handle-se, .resize-handle-nw, .resize-handle-sw { position: absolute; display: block; width: 10px; height: 10px; background: rgba(222,60,80,.9); z-index: 999; } .resize-handle-nw { top: -5px; left: -5px; cursor: nw-resize; } .resize-handle-sw { bottom: -5px; left: -5px; cursor: sw-resize; } .resize-handle-ne { top: -5px; right: -5px; cursor: ne-resize; } .resize-handle-se { bottom: -5px; right: -5px; cursor: se-resize; }

JavaScript

JavaScript мы начинаем с определения некоторых переменных и инициализации Canvas и целевого изображения:

var resizeableImage = function(image_target) { var $container, orig_src = new Image(), image_target = $(image_target).get(0), event_state = {}, constrain = false, min_width = 60, min_height = 60, max_width = 800, max_height = 900, resize_canvas = document.createElement("canvas"); }); resizeableImage($(".resize-image"));

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

Мы также назначаем объект JQuery для элемента контейнера в переменную, чтобы можно было обратиться к ней позже и добавить отслеживатель события mousedown , который определяет, когда кто-то начинает перетаскивать один из маркеров:

var resizeableImage = function(image_target) { // ... init = function(){ // Создаем новое изображение с копией оригинального src // Когда мы изменяем размер изображения, мы всегда берем за основу эту копию orig_src.src=image_target.src; // Добавляем маркеры изменения размера $(image_target).wrap("

") .before("") .before("") .after("") .after(""); // Получаем переменные для контейнера $container = $(image_target).parent(".resize-container"); // Добавляем события $container.on("mousedown", ".resize-handle", startResize); }; //... init(); }

Функции startResize и endResize только указывают браузеру начать отслеживать, куда перемещается мышь, и прекратить отслеживание:

startResize = function(e){ e.preventDefault(); e.stopPropagation(); saveEventState(e); $(document).on("mousemove", resizing); $(document).on("mouseup", endResize); }; endResize = function(e){ e.preventDefault(); $(document).off("mouseup touchend", endResize); $(document).off("mousemove touchmove", resizing); };

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

Мы сохраняем их в переменной с именем event_state и используем ее позже в качестве отправной точки при изменении высоты и ширины:

saveEventState = function(e){ // Сохраняем изначальные параметры события и состояние контейнера event_state.container_width = $container.width(); event_state.container_height = $container.height(); event_state.container_left = $container.offset().left; event_state.container_top = $container.offset().top; event_state.mouse_x = (e.clientX || e.pageX || e.originalEvent.touches.clientX) + $(window).scrollLeft(); event_state.mouse_y = (e.clientY || e.pageY || e.originalEvent.touches.clientY) + $(window).scrollTop(); // Это заплатка для мобильного safari // Почему-то в нем нельзя напрямую копировать сенсорные свойства if(typeof e.originalEvent.touches !== "undefined"){ event_state.touches = ; $.each(e.originalEvent.touches, function(i, ob){ event_state.touches[i] = {}; event_state.touches[i].clientX = 0+ob.clientX; event_state.touches[i].clientY = 0+ob.clientY; }); } event_state.evnt = e; }

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

resizing = function(e){ var mouse={},width,height,left,top,offset=$container.offset(); mouse.x = (e.clientX || e.pageX || e.originalEvent.touches.clientX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY || e.originalEvent.touches.clientY) + $(window).scrollTop(); width = mouse.x - event_state.container_left; height = mouse.y - event_state.container_top; left = event_state.container_left; top = event_state.container_top; if(constrain || e.shiftKey){ height = width / orig_src.width * orig_src.height; } if(width > min_width && height > min_height && width < max_width && height < max_height){ resizeImage(width, height); // Без этого Firefox не будем пересчитывать размеры изображения, пока перетаскивание не завершилось $container.offset({"left": left, "top": top}); } }

Затем мы добавляем опцию для ограничения размеров изображения при переключении с помощью клавиши Shift или переменной.

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

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

Фактическое изменение размеров изображения

Вывести изображение можно просто с помощью drawImage. Мы сначала устанавливаем высоту и ширину холста и всегда используем оригинальную копию полноразмерного изображения. Затем мы используем toDataURL для холста, чтобы получить в кодировке Base64 версию недавно измененного изображения и поместить ее на странице.

В разделе, посвященном обрезке, приведено полное объяснение для всех параметров, которые могут быть использованы с методом drawImage:

resizeImage = function(width, height){ resize_canvas.width = width; resize_canvas.height = height; resize_canvas.getContext("2d").drawImage(orig_src, 0, 0, width, height); $(image_target).attr("src", resize_canvas.toDataURL("image/png")); };

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

Изменение размеров с разных углов

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

Нам нужно обеспечить возможность изменения размеров изображения с любого угла. Для этого мы должны понять поведение нашей демо-модели.

При изменении размеров угол, за который мы тянем, а также прилегающие к нему стороны должны перемещаться, в то время как противоположный угол и прилегающие к нему стороны должны оставаться на месте:

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

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

resizing = function(e){ var mouse={},width,height,left,top,offset=$container.offset(); mouse.x = (e.clientX || e.pageX || e.originalEvent.touches.clientX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY || e.originalEvent.touches.clientY) + $(window).scrollTop(); // Позиция изображения по разному зависит от угла, за который мы тянем if($(event_state.evnt.target).hasClass("resize-handle-se")){ width = mouse.x - event_state.container_left; height = mouse.y - event_state.container_top; left = event_state.container_left; top = event_state.container_top; } else if($(event_state.evnt.target).hasClass("resize-handle-sw")){ width = event_state.container_width - (mouse.x - event_state.container_left); height = mouse.y - event_state.container_top; left = mouse.x; top = event_state.container_top; } else if($(event_state.evnt.target).hasClass("resize-handle-nw")){ width = event_state.container_width - (mouse.x - event_state.container_left); height = event_state.container_height - (mouse.y - event_state.container_top); left = mouse.x; top = mouse.y; if(constrain || e.shiftKey){ top = mouse.y - ((width / orig_src.width * orig_src.height) - height); } } else if($(event_state.evnt.target).hasClass("resize-handle-ne")){ width = mouse.x - event_state.container_left; height = event_state.container_height - (mouse.y - event_state.container_top); left = event_state.container_left; top = mouse.y; if(constrain || e.shiftKey){ top = mouse.y - ((width / orig_src.width * orig_src.height) - height); } } // Опционально поддерживаем соотношение сторон if(constrain || e.shiftKey){ height = width / orig_src.width * orig_src.height; } if(width > min_width && height > min_height && width < max_width && height < max_height){ // Для увеличения производительности вы можете ограничить количество вызовов resizeImage() resizeImage(width, height); // Без этого Firefox не будет пересчитывать размеры изображения, пока перетаскивание не завершилось $container.offset({"left": left, "top": top}); } }

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

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

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

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

init = function(){ //... $container.on("mousedown", "img", startMoving); }

Теперь мы добавляем функции startMoving и endMoving, аналогично тому, как мы добавили startResize и endResize:

startMoving = function(e){ e.preventDefault(); e.stopPropagation(); saveEventState(e); $(document).on("mousemove", moving); $(document).on("mouseup", endMoving); }; endMoving = function(e){ e.preventDefault(); $(document).off("mouseup", endMoving); $(document).off("mousemove", moving); };

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

moving = function(e){ var mouse={}; e.preventDefault(); e.stopPropagation(); mouse.x = (e.clientX || e.pageX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY) + $(window).scrollTop(); $container.offset({ "left": mouse.x - (event_state.mouse_x - event_state.container_left), "top": mouse.y - (event_state.mouse_y - event_state.container_top) }); };

Обрезка изображения

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

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

Для этого нам нужно добавить следующий HTML-код:

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

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

Overlay { position: absolute; left: 50%; top: 50%; margin-left: -100px; margin-top: -100px; z-index: 999; width: 200px; height: 200px; border: solid 2px rgba(222,60,80,.9); box-sizing: content-box; pointer-events: none; } .overlay:after, .overlay:before { content: ""; position: absolute; display: block; width: 204px; height: 40px; border-left: dashed 2px rgba(222,60,80,.9); border-right: dashed 2px rgba(222,60,80,.9); } .overlay:before { top: 0; margin-left: -2px; margin-top: -40px; } .overlay:after { bottom: 0; margin-left: -2px; margin-bottom: -40px; } .overlay-inner:after, .overlay-inner:before { content: ""; position: absolute; display: block; width: 40px; height: 204px; border-top: dashed 2px rgba(222,60,80,.9); border-bottom: dashed 2px rgba(222,60,80,.9); } .overlay-inner:before { left: 0; margin-left: -40px; margin-top: -2px; } .overlay-inner:after { right: 0; margin-right: -40px; margin-top: -2px; } .btn-crop { position: absolute; vertical-align: bottom; right: 5px; bottom: 5px; padding: 6px 10px; z-index: 999; background-color: rgb(222,60,80); border: none; border-radius: 5px; color: #FFF; }

Добавьте в JavaScript следующую функцию и отслеживатель событий:

init = function(){ //... $(".js-crop").on("click", crop); }; crop = function(){ var crop_canvas, left = $(".overlay").offset().left - $container.offset().left, top = $(".overlay").offset().top - $container.offset().top, width = $(".overlay").width(), height = $(".overlay").height(); crop_canvas = document.createElement("canvas"); crop_canvas.width = width; crop_canvas.height = height; crop_canvas.getContext("2d").drawImage(image_target, left, top, width, height, 0, 0, width, height); window.open(crop_canvas.toDataURL("image/png")); }

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

Для обрезки метод холста drawImage требует девять параметров. Первым параметром является источник изображения. Следующие четыре параметра указывают, какая часть исходного изображения используется (бокс отсечения ). Последние четыре параметра указывают, в каком месте холста выводить изображение и в каком размере.

Добавление сенсорных событий

Для mousedown и mouseup существуют эквивалентные сенсорные события — touchstart и touchend , для mousemov есть эквивалентное событие touchmove . Кому-то, кто называл эти события, явно не доставало чувства юмора, иначе он вполне мог бы назвать их “touchdown ” и “touchup ”.

Давайте добавим touchstart и touchend везде, где у нас встречается mousedown и mouseup , а mousemove заменим на touchmove :

// В init()... $container.on("mousedown touchstart", ".resize-handle", startResize); $container.on("mousedown touchstart", "img", startMoving); //В startResize() ... $(document).on("mousemove touchmove", moving); $(document).on("mouseup touchend", endMoving); //В endResize()... $(document).off("mouseup touchend", endMoving); $(document).off("mousemove touchmove", moving); //В startMoving()... $(document).on("mousemove touchmove", moving); $(document).on("mouseup touchend", endMoving); //В endMoving()... $(document).off("mouseup touchend", endMoving); $(document).off("mousemove touchmove", moving);

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

Но так как нам нужно только растяжение, то можно обойтись и без нее. Сейчас я покажу, как легко можно создать растяжение без помощи сторонней библиотеки.

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

Во-первых, мы проверяем, содержит ли событие два «прикосновения » и измеряем расстояние между ними. Отмечаем это как начальное расстояние, а затем измеряем, на сколько это расстояние изменяется во время перемещения. Давайте обновим функцию moving:

moving = function(e){ var mouse={}, touches; e.preventDefault(); e.stopPropagation(); touches = e.originalEvent.touches; mouse.x = (e.clientX || e.pageX || touches.clientX) + $(window).scrollLeft(); mouse.y = (e.clientY || e.pageY || touches.clientY) + $(window).scrollTop(); $container.offset({ "left": mouse.x - (event_state.mouse_x - event_state.container_left), "top": mouse.y - (event_state.mouse_y - event_state.container_top) }); // Отслеживаем растягивание во время перемещения if(event_state.touches && event_state.touches.length > 1 && touches.length > 1){ var width = event_state.container_width, height = event_state.container_height; var a = event_state.touches.clientX - event_state.touches.clientX; a = a * a; var b = event_state.touches.clientY - event_state.touches.clientY; b = b * b; var dist1 = Math.sqrt(a + b); a = e.originalEvent.touches.clientX - touches.clientX; a = a * a; b = e.originalEvent.touches.clientY - touches.clientY; b = b * b; var dist2 = Math.sqrt(a + b); var ratio = dist2 /dist1; width = width * ratio; height = height * ratio; // Для увеличения производительности вы можете ограничить количество вызовов resizeImage() resizeImage(width, height); } };

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

Вот все. Посмотрите еще раз демо-версию или скачайте ZIP-архив .

В ходе тестирования я увидел, что Chrome блокирует реакцию браузера по умолчанию на растягивание, но Firefox работает нормально.

Я надеюсь, что вы нашли эту статью полезной для себя. Я советую вам почитать другие статьи по перетягиваемым элементам и методам загрузки файлов, и узнать, как еще люди объединяют эти методы, чтобы создавать красивые пользовательские интерфейсы.

Данная публикация представляет собой перевод статьи «RESIZING AND CROPPING IMAGES WITH CANVAS » , подготовленной дружной командой проекта

Последние материалы раздела:

Что делать, если завис Мейзу м3 ноте и подобные смартфоны и планшеты на андроиде
Что делать, если завис Мейзу м3 ноте и подобные смартфоны и планшеты на андроиде

Нагревание смартфона Meizu M3 Note до 45-50°C во время зарядки аккумулятора или при длительной работе ресурсоемкого софта является обычным...

Решение проблемы с перегревом Meizu M5 Причины сильного нагревания смартфонов Meizu
Решение проблемы с перегревом Meizu M5 Причины сильного нагревания смартфонов Meizu

Meizu m3 note завис , нагревается и вы не знаете что с ним делать? В этой статье вы узнаете, как сделать принудительную перезагрузку зависшего...

Огромная база данных торрентов, доступных для скачивания
Огромная база данных торрентов, доступных для скачивания

Каталог торрентов на сайте Torrent-Drive.Ru включает в себя все направления, начиная от фильмов и игр для ПК и приставок, заканчивая музыкой,...