СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

H Изменение размера и обрезка изображений с помощью Canvas (Часть 1) в черновиках Перевод

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

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

Демка и исходник

Разметка

HTML будет лаконичен:

<img class="resize-image" src="image.jpg" alt="Image" />


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

Начнем с определения некоторых переменных и инициализации Canvas, и image_target.

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 для переменной Container, что позволяет нам добавить событие MouseDown, чтобы определить, когда кто-то начинает перетаскивать один из маркеров.

var resizeableImage = function(image_target) {
 
// ...
    init = function(){
 
        // Создаем копию оригинального изображения
        // Мы будем использовать эту копию в ходе ресайза
        orig_src.src=image_target.src;
 
        // Добавляем маркеры
        $(image_target).wrap('<div class="resize-container"></div>')
        .before('<span class="resize-handle resize-handle-nw"></span>')
        .before('<span class="resize-handle resize-handle-ne"></span>')
        .after('<span class="resize-handle resize-handle-se"></span>')
        .after('<span class="resize-handle resize-handle-sw"></span>');
 
        //Переменная контейнера
        $container =  $(image_target).parent('.resize-container');
 
        // Добавляем событие
        $container.on('mousedown', '.resize-handle', startResize);
    };
 
//...
 
    init();
}



Функции startResize и endResize отслеживают начало и конец перемещения маркеров.

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[0].clientX) + $(window).scrollLeft(); 
  event_state.mouse_y = (e.clientY || e.pageY || e.originalEvent.touches[0].clientY) + $(window).scrollTop();
 
  // Заплатка для Safari mobile 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 = function(e){ 
    var mouse={},width,height,left,top,offset=$container.offset();
    mouse.x = (e.clientX || e.pageX || e.originalEvent.touches[0].clientX) + $(window).scrollLeft(); 
    mouse.y = (e.clientY || e.pageY || e.originalEvent.touches[0].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});        
    }
}



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

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

Изменение размера изображения
Сначала мы устанавливаем высоту и ширину Canvas и далее используем копию оригинального изображения. Затем мы используем resize_canvas.toDataURL чтобы получить изображение с новыми размерами в кодировке Base64.

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


Работа с методом DrawImage будет подробнее рассмотрена в следующей части.

Слишком просто? Существует одна маленькая оговорка: изображение должно быть на том же домене, что и страница, либо на сервере с включенным CORS (https://ru.wikipedia.org/wiki/Cross-origin_resource_sharing). Иначе вы можете столкнуться с погрешностями или проблемой «испорченного холста».

комментарии (1)

0
alekseev_ap ,  
Вроде несколько дней тому назад аналогичный пост был или на Хабре или на Гиктаймс…