Pull to refresh

Client-side кропалка на canvas

Reading time3 min
Views3.5K
На одном из проектов было необходимо сделать кропалку для загружаемых юзерами аватаров. Стандартные решения, такие как Jcrop, после выделения области отправляют на сервер координаты, и сам кроп изображения необходимо осуществлять уже на сервере. Тем временем, современные браузеры уже дошли до того состояния, когда подобные действия можно осуществлять сразу на клиенте. Это и подтолкнуло меня к написанию своей кропалки с использованием canvas, которая производила бы все действия на клиенте и отправляла готовое изображение в виде base64-строки на сервер. Помимо ускорения работы и разгрузки сервера, это так же позволит нам сразу сменить аватар пользователя на странице, без подгрузки его с сервера.



Технологии и задача


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

Реализация


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

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

В нашем случае для сохранения на сервере используется следующий код (Ruby):
encoded_image = params[:image].sub('data:image/png;base64,', '')
avatar_io = FilelessIO.new(Base64.decode64(encoded_image))
avatar_io.original_filename = "avatar.png" # любое значение, обязательный параметр для CarrierWave
Rails.logger.info "IO: #{avatar_io.inspect}"
@user.avatar = avatar_io
@user.save


При создании кропалке можно передать следующие параметры:
$(this).evroneCrop({
    preview: '#preview1',     //селектор элемента img, в котором отображать превью
    size: {w: 150, h: 150},   //размер результирующего изображения
    ratio: 1,                 //отношение сторон области выделения, в данном случае - квадрат
    setSelect: 'center',      //установка выделения по умолчанию
    log: true                 //вывод отладочной информации
});

Все параметры опциональны. Демо.

setSelect может принимать значение center, автоматически рассчитывая центр изображения и размер выделения, или может принимать объект {x:0, y: 0, w: 100, h: 100} и устанавливать выделение в заданную позицию. Если у вас установлен параметр ratio — высоту в объекте для setSelect передавать не нужно.

ratio принимает число, больше нуля, и фиксирует стороны с отношением длин, равным этому числу. Например, можно передать 1 для квадрата или 16/9 для отношения сторон 16 к 9.

Браузерные заморочки

Получившийся инструмент работает во всех мажорных браузерах — Chrome, Firefox > 4, Safari, Opera >11, IE 9. В планах есть написание fallback'а во флэш для IE8. Изначально планировалось использовать гугловый excanvas, но тот, как оказалось, не предоставляет такого необходимого для нашей задачи метода, как toDataURL(); Есть выход с реализацией на флеше, но до него руки пока не дошли.

Кропалка еще сыровата, например, до сих пор не реализована установка минимального и максимального размера, но она лежит на гитхабе, работает, и потихоньку развивается.
Tags:
Hubs:
Total votes 39: ↑35 and ↓4+31
Comments13

Articles