# Bash
rails g migration add_avatar_to_users avatar
rails g uploader avatar
# Gemfile
gem 'mini_magick'
gem 'carrierwave'
gem 'rails-assets-jcrop', source: 'https://rails-assets.org'
# application.js
//= require jcrop
# application.css
*= require jcrop
# users_controller.rb
def create
@user = User.new(user_params)
if @user.save
if params[:user][:avatar].present?
render :crop
else
redirect_to @user, notice: "Successfully created user."
end
else
render :new
end
end
def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
if params[:user][:avatar].present?
render :crop
else
redirect_to @user, notice: "Successfully updated user."
end
else
render :new
end
end
...
def user_params
params.require(:user).permit(:name, :avatar, :crop_x, :crop_y, :crop_w, :crop_h)
end
# user.rb
class User < ApplicationRecord
mount_uploader :avatar, AvatarUploader
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
after_update :crop_avatar
def crop_avatar
avatar.recreate_versions! if crop_x.present?
end
end
# uploaders/avatar_uploader.rb
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
version :thumb do
process :crop
resize_to_fill(100, 100)
end
version :tiny, from_version: :thumb do
process resize_to_fill: [20, 20]
end
version :large do
resize_to_limit(600, 600)
end
def crop
if model.crop_x.present?
resize_to_limit(600, 600)
manipulate! do |img|
x = model.crop_x.to_i
y = model.crop_y.to_i
w = model.crop_w.to_i
h = model.crop_h.to_i
# [[w, h].join('x'),[x, y].join('+')].join('+') => "wxh+x+y"
img.crop([[w, h].join('x'),[x, y].join('+')].join('+'))
end
end
end
end
# users.coffee
$ ->
new AvatarCrop()
class AvatarCrop
constructor: ->
width = parseInt($('#cropbox').width())
height = parseInt($('#cropbox').height())
$('#cropbox').Jcrop
aspectRatio: 1
setSelect: [0, 0, width, height]
onSelect: @update
onChange: @update
update: (coords) =>
$('#user_crop_x').val(coords.x)
$('#user_crop_y').val(coords.y)
$('#user_crop_w').val(coords.w)
$('#user_crop_h').val(coords.h)
@updatePreview(coords)
updatePreview: (coords) =>
rx = 100 / coords.w
ry = 100 / coords.h
$('#preview').css
width: Math.round(rx * $('#cropbox').width()) + 'px'
height: Math.round(ry * $('#cropbox').height()) + 'px'
marginLeft: '-' + Math.round(rx * coords.x) + 'px'
marginTop: '-' + Math.round(ry * coords.y) + 'px'
# users.js
var AvatarCrop,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
$(function() {
return new AvatarCrop();
});
AvatarCrop = (function() {
function AvatarCrop() {
this.updatePreview = bind(this.updatePreview, this);
this.update = bind(this.update, this);
var height, width;
width = parseInt($('#cropbox').width());
height = parseInt($('#cropbox').height());
$('#cropbox').Jcrop({
aspectRatio: 1,
setSelect: [0, 0, width, height],
onSelect: this.update,
onChange: this.update
});
}
AvatarCrop.prototype.update = function(coords) {
$('#user_crop_x').val(coords.x);
$('#user_crop_y').val(coords.y);
$('#user_crop_w').val(coords.w);
$('#user_crop_h').val(coords.h);
return this.updatePreview(coords);
};
AvatarCrop.prototype.updatePreview = function(coords) {
var rx, ry;
rx = 100 / coords.w;
ry = 100 / coords.h;
return $('#preview').css({
width: Math.round(rx * $('#cropbox').width()) + 'px',
height: Math.round(ry * $('#cropbox').height()) + 'px',
marginLeft: '-' + Math.round(rx * coords.x) + 'px',
marginTop: '-' + Math.round(ry * coords.y) + 'px'
});
};
return AvatarCrop;
})();
# crop.html.erb
<div class='row'>
<div class='col-xs-8'>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Crop Avatar</h3>
</div>
<div class="panel-body">
<%= image_tag @user.avatar_url(:large), id: "cropbox" %>
<br>
<%= simple_form_for(@user) do |f| %>
<% %w[x y w h].each do |attribute| %>
<%= f.input "crop_#{attribute}", as: :hidden %>
<% end %>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
</div>
</div>
</div>
<div class='col-xs-4'>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Preview</h3>
</div>
<div class="panel-body">
<div style="width:100px; height:100px; overflow:hidden">
<%= image_tag @user.avatar.url(:large), id: "preview" %>
</div>
</div>
</div>
</div>
</div>