Skip to content

Instantly share code, notes, and snippets.

@joseh-henrique
Forked from gilcierweb/_form.html.erb
Created February 21, 2021 18:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save joseh-henrique/ccf00fa1bfddfe7cd5015551d049b85b to your computer and use it in GitHub Desktop.
Save joseh-henrique/ccf00fa1bfddfe7cd5015551d049b85b to your computer and use it in GitHub Desktop.
Upload image crop with Ruby and Rails, CarrierWave and Cropper.js
#app/views/businesses/_form.html.erb
<%= form_with(model: business, scope: :business, local: true, :html => {multipart: true}) do |form| %>
<div class="row">
<div class="col-md-12">
<%= form.file_field :logo_image, id: :business_logo_image %>
<%= form.label :logo_image, class: 'upload' do %>
<i class="material-icons">file_upload</i>
<span>Choose image</span>
<% end %>
</div>
<% %w[x y w h].each do |attribute| %>
<%= form.hidden_field "logo_crop_#{attribute}", id:"logo_crop_#{attribute}" %>
<% end %>
</div>
<style>
img {
max-width: 100%;
}
</style>
<!-- Modal -->
<div class="modal fade" id="upload-modal" aria-labelledby="modalLabel" role="dialog" tabindex="-1">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalLabel">Cut logo</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div>
<img id="image" width="100%" src="" alt="Logo">
</div>
<p class="text-center">
<button type="button" class="btn btn-primary rotate" data-method="rotate" data-option="-30">
<i class="fa fa-undo"></i></button>
<button type="button" class="btn btn-primary rotate" data-method="rotate" data-option="30">
<i class="fa fa-repeat"></i></button>
</p>
<div id="result"></div>
<div id="preview"></div>
<div>
<canvas id="canvas">
Your browser does not support the HTML5 canvas element.
</canvas>
</div>
</div>
<div class="modal-footer">
<input type="button" id="btnCrop" value="Crop" />
<input type="button" id="btnRestore" value="Restore" />
<input type="button" class="btn btn-primary" id="button" value="Cut" data-dismiss="modal"></button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<%= form.submit t('form.submit'), class: 'btn btn-primary' %>
<% end %>
#app/models/business.rb
class Business < ApplicationRecord
mount_uploader :logo_image, LogoImageUploader
end
#app/controllers/businesses_controller.rb
class BusinessesController < ApplicationController
before_action :set_business, only: [:show, :edit, :update, :destroy]
before_action :sanitize_fields_params, only: [:create, :update]
...
private
def sanitize_fields_params
$logo_crop_x = 0
$logo_crop_y = 0
$logo_crop_w = 0
$logo_crop_h = 0
$logo_crop_x = params[:business][:logo_crop_x]
$logo_crop_y = params[:business][:logo_crop_y]
$logo_crop_w = params[:business][:logo_crop_w]
$logo_crop_h = params[:business][:logo_crop_h]
end
def business_params
params.require(:business).permit( :name, :logo_image, :logo_crop_x, :logo_crop_y, :logo_crop_w, :logo_crop_h)
end
end
// #app/assets/javascripts/cropper_custom.js
// https://fengyuanchen.github.io/cropperjs/
$(function () {
function crop_image_load(data) {
var $crop_x = $("input#logo_crop_x"),
$crop_y = $("input#logo_crop_y"),
$crop_w = $("input#logo_crop_w"),
$crop_h = $("input#logo_crop_h");
$crop_x.val('');
$crop_y.val('');
$crop_h.val('');
$crop_w.val('');
$crop_x.val(accounting.toFixed(data.x, 6));
$crop_y.val(accounting.toFixed(data.y, 6));
$crop_h.val(accounting.toFixed(data.height, 6));
$crop_w.val(accounting.toFixed(data.width, 6));
// $crop_x.val(Math.round(data.x));
// $crop_y.val(Math.round(data.y));
// $crop_h.val(Math.round(data.height));
// $crop_w.val(Math.round(data.width));
}
var $crop_x = $("input#logo_crop_x"),
$crop_y = $("input#logo_crop_y"),
$crop_w = $("input#logo_crop_w"),
$crop_h = $("input#logo_crop_h");
$crop_x.val('');
$crop_y.val('');
$crop_h.val('');
$crop_w.val('');
var $image = $('#image');
var $button = $('#button');
var $result = $('#result');
var croppable = false;
var cropBoxData;
var canvasData;
var img = new Image();
var img_tag = $('#business_logo_image').parent().find("#preview_avatar").children("img");
$('#upload-modal').on('shown.bs.modal', function () {
$image.cropper({
preview: '#preview',
viewMode: 1,
dragMode: 'move',
aspectRatio: 16 / 9,
autoCrop: true,
autoCropArea: 0.65,
restore: false,
guides: false,
highlight: false,
cropBoxMovable: false,
cropBoxResizable: false,
scalable: false,
zoomable: false,
rotatable: false,
getData: true,
checkCrossOrigin: true,
modal: true,
center: true,
// allowMove : true,
// allowResize : true,
// allowSelect : true,
// minSelect : [0, 0],
// outlineOpacity : 0.5,
// overlayOpacity : 0.5,
// selectionPosition : [0, 0],
// selectionWidth : 0,
// selectionHeight : 0,
ready: function () {
$image.cropper('setCanvasData', canvasData);
$image.cropper('setCropBoxData', cropBoxData);
},
crop: function (event) {
crop_image_load(event)
console.log(event.x);
console.log(event.y);
console.log(event.width);
console.log(event.height);
console.log(event.rotate);
console.log(event.scaleX);
console.log(event.scaleY);
}
});
}).on('hidden.bs.modal', function () {
cropBoxData = $image.cropper('getCropBoxData');
canvasData = $image.cropper('getCanvasData');
img.src = img_tag;
// cropImage();
$image.cropper('destroy');
});
$button.on('click', function () {
var croppedCanvas;
var roundedCanvas;
if (!croppable) {
return;
}
// Crop
croppedCanvas = $image.cropper('getCroppedCanvas');
// Round
roundedCanvas = getRoundedCanvas(croppedCanvas);
//console.log('<img src="' + roundedCanvas.toDataURL() + '">')
// Show
$result.html('<img src="' + roundedCanvas.toDataURL() + '">');
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#image').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
$("#business_logo_image").change(function () {
// console.log(this)
$('#upload-modal').modal('show');
readURL(this);
var canvas = $("#canvas"),
context = canvas.get(0).getContext("2d"),
$result = $('#result');
if (this.files && this.files[0]) {
if ( this.files[0].type.match(/^image\//) ) {
var reader = new FileReader();
reader.onload = function(evt) {
var img = new Image();
img.onload = function() {
context.canvas.height = img.height;
context.canvas.width = img.width;
context.drawImage(img, 0, 0);
var cropper = canvas.cropper({
aspectRatio: 16 / 9
});
$('#btnCrop').click(function() {
console.log(evt)
crop_image_load(evt)
// Get a string base 64 data url
var croppedImageDataURL = canvas.cropper('getCroppedCanvas').toDataURL("image/png");
$result.append( $('<img>').attr('src', croppedImageDataURL) );
});
$('#btnRestore').click(function() {
canvas.cropper('reset');
$result.empty();
});
};
img.src = evt.target.result;
};
reader.readAsDataURL(this.files[0]);
}
else {
alert("Invalid file type! Please select an image file.");
}
}
else {
alert('No file(s) selected.');
}
});
var canvas = $("#canvas"),
context = canvas.get(0).getContext("2d"),
$result = $('#result');
$('#fileInput').on( 'change', function(){
if (this.files && this.files[0]) {
if ( this.files[0].type.match(/^image\//) ) {
var reader = new FileReader();
reader.onload = function(evt) {
var img = new Image();
img.onload = function() {
context.canvas.height = img.height;
context.canvas.width = img.width;
context.drawImage(img, 0, 0);
var cropper = canvas.cropper({
aspectRatio: 16 / 9
});
$('#btnCrop').click(function() {
// Get a string base 64 data url
var croppedImageDataURL = canvas.cropper('getCroppedCanvas').toDataURL("image/png");
$result.append( $('<img>').attr('src', croppedImageDataURL) );
});
$('#btnRestore').click(function() {
canvas.cropper('reset');
$result.empty();
});
};
img.src = evt.target.result;
};
reader.readAsDataURL(this.files[0]);
}
else {
alert("Invalid file type! Please select an image file.");
}
}
else {
alert('No file(s) selected.');
}
});
});
#app/uploaders/logo_image_uploader.rb
class LogoImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# process resize_to_fit: [800, 800]
# process :crop_image
version :large do
process :crop_image
process resize_to_fit: [800, 800]
# resize_to_limit(600, 600)
end
version :medium do
process :crop_image
# process resize_to_fit: [600, 600]
resize_to_limit(600, 600)
end
version :thumb do
process :crop_image
resize_to_fill(100, 100)
end
version :tiny, from_version: :thumb do
process resize_to_fill: [20, 20]
end
def crop_image
resize_to_limit(600, 600)
unless $logo_crop_x.blank?
manipulate! do |image|
x = $logo_crop_x.to_f
y = $logo_crop_y.to_f
w = $logo_crop_w.to_f
h = $logo_crop_h.to_f
#img.crop "#{w}x#{h}+#{x}+#{y}"
image.crop([[w, h].join('x'), [x, y].join('+')].join('+'))
end
end
end
def extension_white_list
%w(jpg jpeg gif png)
end
def filename
"#{secure_token(10)}.#{file.extension}" if original_filename.present?
end
protected
def secure_token(length = 16)
return SecureRandom.hex(length / 2)
end
end
#Steps
rails new cropper-image-rails
##edit file Gemfile
#cropper-image-rails/Gemfile
gem 'carrierwave'
gem 'mini_magick'
bundle install
rails generate scaffold business name:string description:text logo_image:string
rails generate uploader LogoImage
rails db:create
rails db:migrate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment