23.05.2022

Css обрезать изображение по высоте косой. CSS: вписываем изображение в область


Узнайте, как изменять размер и обрезать изображения с помощью 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 » , подготовленной дружной командой проекта

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

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

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

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

С этой целью мы начинаем работу!

Разметка

В нашем демо мы будем работать с одним заданным изображением:

Все! Другого HTML нам не нужно.

CSS

CSS-код тоже будет не очень большим. Определим стили для resize-container и самого изображения.

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 handles’. Это небольшие квадратики, находящиеся в углах изображений, которые мы перетаскиваем для изменения размеров картинки.

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

Начнем с создания переменной и полотна в 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"));

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

Var resizeableImage = function(image_target) { // ... init = function(){ // Create a new image with a copy of the original src // When resizing, we will always use this original copy as the base orig_src.src=image_target.src; // Add resize handles $(image_target).wrap("

") .before("") .before("") .after("") .after(""); // Get a variable for the container $container = $(image_target).parent(".resize-container"); // Add events $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){ // Save the initial event details and container state 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(); // This is a fix for mobile safari // For some reason it does not allow a direct copy of the touches property 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); // Without this Firefox will not re-calculate the the image dimensions until drag end $container.offset({"left": left, "top": top}); } }

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

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

Новые размеры изображения

Рисовать изображения в Canvas так же просто, как и drawImage . Мы задаем высоту и длину картинки, а затем предоставляем оригинал. Также используем toDataURL для получения Base64-encoded версии результата операции.

Приведены все объяснения доступных для данной операции параметров.

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")); };

Слишком просто? Есть одна оговорка: изображение должно быть размещено на одном домене с нашей страницей, либо на сервере должна быть активирована функция . Если это не так, то у Вас возникнут проблемы с ‘tainted canvas’.

Увеличение через другие вершины

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

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

Мы можем поменять код так, чтобы при растягивании картинки за любой угол она менялась. Давайте обновим функцию 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(); // Position image differently depending on the corner dragged and constraints 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); } } // Optionally maintain aspect ratio 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){ // To improve performance you might limit how often resizeImage() is called resizeImage(width, height); // Without this Firefox will not re-calculate the the image dimensions until drag end $container.offset({"left": left, "top": top}); } }

Теперь мы проверяем, какой из resize-handle был задействован, и применяем необходимые изменения.

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

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

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 в canvas. Первый - исходное изображение. Следующие четыре - место, задействованное под операцию. Еще четыре - координаты места, в котором стоит начать рисование в canvas, и какого размера будет изображение.

Добавление прикосновений и распознавания жестов

Мы создали поддержку мышки. Давайте не будем обделять вниманием и мобильные устройства.

Для mousedown и mouseup есть эквивалентные операторы - touchstart и touchend , а для mousemove есть touchmove . Нужно быть внимательным, чтобы не перепутать их с touchup и touchdown (А то будет смешно).

Давайте добавим touchstart и touchend везде, где у нас есть mousedown , и mouseup вместе с touchmove туда, где есть mousemove .

// In init()... $container.on("mousedown touchstart", ".resize-handle", startResize); $container.on("mousedown touchstart", "img", startMoving); //In startResize() ... $(document).on("mousemove touchmove", moving); $(document).on("mouseup touchend", endMoving); //In endResize()... $(document).off("mouseup touchend", endMoving); $(document).off("mousemove touchmove", moving); //In startMoving()... $(document).on("mousemove touchmove", moving); $(document).on("mouseup touchend", endMoving); //In 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) }); // Watch for pinch zoom gesture while moving 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; // To improve performance you might limit how often resizeImage() is called resizeImage(width, height); } };

На основе этих данных мы уменьшаем или увеличиваем наше изображение и корректируем его высоту и длину.

На этом все. Вы можете открыть

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

С помощью свойства clip можно обрезать элемент по заданным размерам.

1. Свойство overflow

Содержимое блочных элементов может переполнять блок в случае, когда для блока явно задана высота и/или ширина. Без указания высоты блок будет растягиваться, чтобы вместить содержимое, кроме случаев, когда для блока задано позиционирование position: absolute; или position: fixed; . Текст может переполнять блок по высоте, изображения — по высоте и ширине.

Значения:
visible Значение по умолчанию. Весь контент становится видимым независимо от размеров блока-контейнера. Возможно перекрытия содержимого соседних блоков.
scroll Добавляет полосы прокрутки внутри области отображения элемента, которые отображаются даже в случае, когда содержимое по размерам помещается внутри блока. Размеры контейнера не меняются.
auto Добавляет полосы прокрутки только в случае необходимости.
hidden Скрывает содержимое, выходящее за границы блока. Может скрыть часть содержимого. Используется для блоков-контейнеров, содержащих плавающие элементы. Также предотвращает отображение фона или границ под плавающими элементами (для которых задано свойство float: left / right; . При этом размеры контейнера не изменяются.
Рис. 1. Обрезка содержимого блока с помощью свойства overflow Синтаксис: div { width: 200px; height: 150px; overflow: hidden; }

2. Свойство overflow-x

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

Синтаксис:

Div { overflow-x: hidden; }

3. Свойство overflow-y

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

Синтаксис:

Div { overflow-y: hidden; }

4. Свойство clip

Свойство определяет, какая часть элемента будет видна. Часть элемента, которая останется видимой после обрезки, называется областью отсечения. Отсечение применяется к элементу, который изначально полностью видимый. Свойство применяется к элементам, для которых установлено свойство position со значениями absolute или fixed .

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

Способ 0: 100% width/height

Первый способ заключается в использовании значения 100% для одного из параметров тега img – ширины или высоты. При этом второй параметр должен быть установлен в auto для сохранения пропорций изображения. Картинка растянется до размера контейнера по одному из измерений, а второе значение будет рассчитано автоматически. В результате по краям картинки могут образоваться поля, но она поместится в отведённой области целиком, без обрезки.

Так как при высоте 100% от высоты контейнера изображение вылезает за пределы этого контейнера по ширине, для обрезки лишнего используется свойство overflow со значением hidden. При этом, если мы хотим, чтобы в видимую область попала центральная часть изображения, его надо выровнять по центру контейнера. Проще всего это сделать задав контейнеру display: flex, и далее позиционировать изображение по вертикали и горизонтали с помощью свойств justify-content и align-items.

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

Чтобы избавиться от полей можно заменить свойства width и height на min-width и min-height (при этом ширина и высота по умолчанию примут значения auto). Тогда вне зависимости от ориентации изображения, оно заполнит область целиком.

Важно: если вы используете выравнивание с помощью flex-контейнера, добавьте flex-shrink: 0, чтобы запретить автоматическое масштабирование изображения.

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

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

Способ 2: background-image

Другой способ вставки изображений – установка их в качестве фонового изображения блока. Этот способ попал сюда за то, что у фонового изображения есть свойство background-size с двумя полезными значениями: contain и cover. Первое умещает изображение целиком с сохранением пропорций (и образованием полей), второе растягивает изображение таким образом, чтобы полей не осталось, также с сохранением пропорций и при необходимости обрезая лишнее.

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

Способ 3: object-fit

Этот способ похож на предыдущий, только не нужно делать изображение фоном. Свойство object-fit применяется к самому изображению и имеет значения contain и cover, работающие аналогично таким же значениям свойства background-size. При этом width и height нужно установить в 100%.

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

Как удалить не нужные куски изображения

Поглядим, как производится кадрирование фактически, удалив неширокую черную полосу у нижнего края фото, которую не удалось в достаточной степени осветлить. Кадрирование производится особым инвентарем Crop Tool (С) (Инструмент «Рамка» (С)).

Нажмите кнопку Crop Tool (С) (Инструмент «Рамка» (С)) на панели инструментов (Tools), чтобы избрать этот инструмент.

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

Нажмите и удерживайте левую кнопку мыши.

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

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

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

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

Два раза щелкните мышью внутри кадрирующей рамки либо нажмите кнопку Enter. Фото будет обрезана по установленной границе.

Как уменьшить размер изображения

Но даже после обрезки фото довольно велика для WEB странички. При разрешении экрана 800 x 600 пикселов (а с таким разрешением по статистике в текущее время работает большая часть пользователей Web) фото занимает существенное место на дисплее. Не считая того, файл с изображением такового размера будет довольно велик по объему и будет длительно загружаться. Поэтому уменьшим размер изображения, что, в свою очередь, уменьшит объем файла.

Изберите команду меню Image - Image Size (Изображение - Размер изображения). На дисплее появится диалог Image Size (Размер изображения).

В высшей части диалога, в группе частей управления Pixel Dimensions (Размерность в пикселах) указан текущий размер файла в формате PSD - 400.3К, также текущие ширина (Width) и высота (Height) изображения в пикселах. У вас эти значения после кадрирования могут быть несколько другими.

В группе частей управления Document Size (Размер документа) указаны размеры документа в сантиметрах и его графическое разрешение (Resolution). При установленном флаге Constrain Proportions (Сохранить пропорции) программа автоматом сохраняет пропорции изображения при изменении одного из размеров - ширины либо высоты.

При изменении размеров изображения в группе частей управления Pixel Dimensions (Размерность в пикселах) соответственно меняются его свойства для документа - или его размеры, или графическое разрешение, зависимо от того, установлен либо сброшен флаг Resample Image (Поменять размер). Если этот флаг установлен, то меняется размер документа и, соответственно, объем файла с изображением. Если же флаг сброшен, то можно поменять только размеры документа. При всем этом, соответственно, будет изменяться его графическое разрешение, а размеры изображения в пикселах и объем файла останутся без конфигурации.

При уменьшении размерности либо графического разрешения Adobe Photoshop удаляет из изображения сверхизбыточную информацию, а при увеличении этих характеристик - сформировывает недостающую информацию на базе цветовых величин имеющихся пикселов. В обоих случаях программа употребляет один из 5 способов интерполяции, который можно избрать в открывающемся перечне Resample Image (Поменять размер).

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

Удостоверьтесь, что установлен флаг Resample Image (Поменять размер), чтобы сохранить графическое разрешение изображения и поменять его размер.

В поле ввода Width (Ширина) группы частей управления Pixel Dimensions (Размерность в пикселах) введите новое значение ширины изображения в пикселах - 300. Автоматом поменяется значение высоты изображения в поле ввода Height (Высота), также размер документа в группе частей управления Document Size (Размер документа). В высшей части диалога вы увидите также новое, уменьшенное значение объема файла и рядом с ним, в скобках прежний объем.

Закройте диалог Image Size (Размер изображения), нажав кнопку ОК. Установленные характеристики будут использованы, и размер изображения в окне документа уменьшится.

Как избежать утраты свойства изображения при изменении его размера

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

Изберите команду меню Filter - Sharpen - Unsharp Mask (Фильтр - Резкость - Контурная резкость). На дисплее появится диалог Unsharp Mask (Контурная резкость).

Перемещая ползунковый регулятор Amount (Эффект), восстановите резкость фото. Значение этого параметра должно быть в границах 50-60%.

Нажатием кнопки OK закройте диалог Urisharp Mask (Контурная резкость). Резкость изображения усилится.

Сохраните документ, выбрав команду меню File - Save (Файл - Сохранить).

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