Crop, zoom and rotate image using carrierwave
We already have a good reading about cropping in rails using jcrop from ryanbates railscast (http://railscasts.com/episodes/182-cropping-images?view=asciicast). But in my recent rails project I need functionality to rotate and crop zoomed image. So I made following changes that might help others.
## My original image :-
## My original image :-
First of all we need carrierwave to upload images and mini_magick to manipulate images. So add following gems in your Gemfile.
##/Gemfile
gem 'carrierwave'
gem 'mini_magick'
To crop, zoom and rotate image we need to add 'cropzoom' file and 'custom.min' file in assets. Find here https://github.com/cropzoom/cropzoom
Following assetes changes required :
##/app/assets/javascripts/application.js
//= require jquery.cropzoom
//= require jquery-ui-1.10.3
##/app/assets/stylesheets/application.css
*= require jquery-ui-1.10.3.custom.min
*= require jquery.cropzoom
Now, to get coordinates of crop, zoom or rotated image we need to set attr_accessor for :
Cropped image coordinates : crop_x, crop_y, crop_w, crop_h
Zoomed image coordinates : zoom_x, zoom_y, zoom_w, zoom_h
Dragged image coordinates : drag_x, drag_y
Rotation angle : rotation_angle
So, my model looks like this :-
/app/models/user.rb
class User < ActiveRecord::Base
mount_uploader :image, ImageUploader
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h, :rotation_angle, :zoom_w, :zoom_h, :zoom_x, :zoom_y, :drag_x, :drag_y
Changes required in image crop view.
##/app/views/users/crop.html.erb
# Hide the original image.
<%= image_tag @user.image.url, class: 'img-responsive hide_img' %>
# Container where we will crop, zoom and rotate image
<div id='crop_container'></div>
<% form_for @user do |f| %>
<% for attribute in [:zoom_w, :zoom_h, :zoom_x, :zoom_y, :drag_x, :drag_y, :rotation_angle, :crop_x, :crop_y, :crop_w, :crop_h] %>
<%= f.hidden_field attribute, :id => attribute %>
<% end %>
<p><%= f.submit "Crop" %></p>
<% end %>
# Initialize cropzoom container
<script>
$(document).ready(function() {
var cropzoom = $('#crop_container').cropzoom({
width:300,
height:300,
bgColor: '#CCC',
enableRotation:true,
enableZoom:true,
zoomSteps:10,
rotationSteps:10,
selector:{
centered:false,
startWithOverlay: true,
borderColor:'blue',
borderColorHover:'yellow'
},
image:{
source:$('.img-responsive').attr('src'),
width:768,
height:768,
minZoom: 20,
onRotate: function(imageObject, rotate_angle){
// Get rotatation angle
$("#rotation_angle").val(rotate_angle);
},
onZoom: function(imageObject, dimensions){
// Get zoom coordinates
$('#zoom_w').val(dimensions.w);
$('#zoom_h').val(dimensions.h);
$('#zoom_x').val(dimensions.posX);
$('#zoom_y').val(dimensions.posY);
// Set drag coordinates to zero when image is zoomed
$('#drag_x').val(0);
$('#drag_y').val(0);
},
onImageDrag: function(imageObject, position){
// Get dragged image coordinates
$('#drag_x').val(position.posX);
$('#drag_y').val(position.posY);
// Set zoom x-y coordinates to zero when image is dragged
$('#zoom_x').val(0);
$('#zoom_y').val(0);
}
}
});
});
// Get cropped image coordinates
$(document).on('click', 'input[type="submit"]', function() {
var get_html = $('#infoSelector').html()
var get_array = get_html.split('|')
var get_x_y_coords_array = get_array[0].split('-')
var get_w_h_coords_array = get_array[1].split('-')
var get_x_coord = get_x_y_coords_array[0].split(':')[1]
var get_y_coord = get_x_y_coords_array[1].split(':')[1]
var get_w_coord = get_w_h_coords_array[0].split(':')[1]
var get_h_coord = get_w_h_coords_array[1].split(':')[1]
$('#crop_x').val(get_x_coord);
$('#crop_y').val(get_y_coord);
$('#crop_w').val(get_w_coord);
$('#crop_h').val(get_h_coord);
});
</script>
Crop view looks like this :-
Note :- In documentation of cropzoom plugin 'onImageDrag()' shuold have two values, but it have only one. Line no. 182 in my case
So, We need to change in jquery.cropzoom.js.
##/app/assets/javascripts/jquery.cropzoom.js
Change in $($image).draggable() function.
Change :-
if ($options.image.onImageDrag != null)
$options.image.onImageDrag($image);
To :-
if ($options.image.onImageDrag != null)
$options.image.onImageDrag($image, getData('image'));
Required controller changes.
##/app/controllers/users_controller.rb
@user = User.find(params[:id])
#If we get image from remotely
@user.remote_image_url = User.first.image
if @user.update_attributes(params[:user])
if params[:user][:crop_x].present?
@user.image = @user.image.resize_and_crop
@user.save!
@user.image.recreate_versions!
end
end
##/app/assets/stylesheets/custom.css
// To hide image
.hide_img {
display: none;
}
Now, finally changes are required in carrierwave uploader.
##/app/uploaders/image_uploader.rb
include CarrierWave::MiniMagick
version :custom_crop do
process :resize_and_crop
end
def resize_and_crop
if model.class.to_s == "User"
if model.crop_x.present?
manipulate! do |img|
w = model.crop_w.to_i
h = model.crop_h.to_i
// Set x-y coordinates of cropped image.
x = model.zoom_x.to_i >= 0 ? (model.crop_x.to_i - model.zoom_x.to_i) : (model.zoom_x.to_i.abs + model.crop_x.to_i)
y = model.zoom_y.to_i >= 0 ? (model.crop_y.to_i - model.zoom_y.to_i) : (model.zoom_y.to_i.abs + model.crop_y.to_i)
x = model.drag_x.to_i >= 0 ? (x - model.drag_x.to_i) : (model.drag_x.to_i.to_i.abs + x)
y = model.drag_y.to_i >= 0 ? (y - model.drag_y.to_i) : (model.drag_y.to_i.to_i.abs + y)
img.combine_options do |i|
// First we need to resize image with zoomed image. For more details you can find here "https://github.com/minimagick/minimagick"
i.resize "#{model.zoom_w.to_i}x#{model.zoom_h.to_i}+#{model.zoom_x.to_i}+#{model.zoom_y.to_i}^\!"
// Rotate zoomed image
i.rotate(model.rotation_angle.to_i)
// Crop zoomed and rotated image
i.crop "#{w}x#{h}+#{x}+#{y}"
end
img
end
end
end
end
## Final output is :-
I'm on the fence about this, while more customization is good, I have a feeling this is a "in-progress" update, it just feels incomplete and half-way there.
ReplyDeleteWe use badge layout for apps on design approvals (visual projects), so the image being displayed is important. Old layout "feels like" it had larger images,
maybe because the images were cropped more loosely so it's easier to tell which project it was at quick glance. Now the image is cropped closer, making it
harder to scan thru at quick glance. I find myself needing to click into the project more often than usual. Which makes the whole user experience less
efficient.
I have a couple suggestions that might make it work better:
1. Increase the height of the window the cover image is being displayed.
2. Let us to choose which image to be displayed as "cover" (like how Pinterest handles cover images of each board, was hoping for this for a long time)
3. Let us adjust which part of the image to show and how tight or loose the crop is (with a fixed window, let us move the image around and maybe enlarge or
shrink it to control what shows thru the window. Pinterest does a limited form of this, which is very useful in making the cover image relevant)
4. Allow Cover Image to be ordered in different hierarchy (currently every element can be ordered differently except the Cover Image, it seems to be stuck
in the 2nd spot, would like the option to set it on another spot in the layout. This one seems like an easy fix, since you guys allow that for every other
element already)
I'm on the fence about this, while more customization is good, I have a feeling this is a "in-progress" update, it just feels incomplete and half-way there.
ReplyDeleteWe use badge layout for apps on design approvals (visual projects), so the image being displayed is important. Old layout "feels like" it had larger images,
maybe because the images were cropped more loosely so it's easier to tell which project it was at quick glance. Now the image is cropped closer, making it
harder to scan thru at quick glance. I find myself needing to click into the project more often than usual. Which makes the whole user experience less
efficient.
I have a couple suggestions that might make it work better:
1. Increase the height of the window the cover image is being displayed.
2. Let us to choose which image to be displayed as "cover" (like how Pinterest handles cover images of each board, was hoping for this for a long time)
3. Let us adjust which part of the image to show and how tight or loose the crop is (with a fixed window, let us move the image around and maybe enlarge or
shrink it to control what shows thru the window. Pinterest does a limited form of this, which is very useful in making the cover image relevant)
4. Allow Cover Image to be ordered in different hierarchy (currently every element can be ordered differently except the Cover Image, it seems to be stuck
in the 2nd spot, would like the option to set it on another spot in the layout. This one seems like an easy fix, since you guys allow that for every other
element already)
I'm on the fence about this, while more customization is good, I have a feeling this is a "in-progress" update, it just feels incomplete and half-way there.
ReplyDeleteWe use badge layout for apps on design approvals (visual projects), so the image being displayed is important. Old layout "feels like" it had larger images,
maybe because the images were cropped more loosely so it's easier to tell which project it was at quick glance. Now the image is cropped closer, making it
harder to scan thru at quick glance. I find myself needing to click into the project more often than usual. Which makes the whole user experience less
efficient.
I have a couple suggestions that might make it work better:
1. Increase the height of the window the cover image is being displayed.
2. Let us to choose which image to be displayed as "cover" (like how Pinterest handles cover images of each board, was hoping for this for a long time)
3. Let us adjust which part of the image to show and how tight or loose the crop is (with a fixed window, let us move the image around and maybe enlarge or
shrink it to control what shows thru the window. Pinterest does a limited form of this, which is very useful in making the cover image relevant)
4. Allow Cover Image to be ordered in different hierarchy (currently every element can be ordered differently except the Cover Image, it seems to be stuck
in the 2nd spot, would like the option to set it on another spot in the layout. This one seems like an easy fix, since you guys allow that for every other
element already)