На одном из проектов было необходимо сделать кропалку для загружаемых юзерами аватаров. Стандартные решения, такие как 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(); Есть выход с реализацией на флеше, но до него руки пока не дошли.
Кропалка еще сыровата, например, до сих пор не реализована установка минимального и максимального размера, но она лежит на гитхабе, работает, и потихоньку развивается.
Арсений Заречнев, Evrone.com.