3

Once I used carrierwave and Jcrop to upload and crop user picture , it works well . User can free clipping after then upload the picture.But now I want to chang the upload method from carrierwave to ActiveStorage. At this time I also want to free clipping picture. Then how could I chang my codes? Thanks so much!!

My part of gems :

gem 'rails', '~> 5.2'
gem 'mini_magick'

# gem 'carrierwave', '~> 1.2', '>= 1.2.2'**(No use in ActiveStorage)**

Part of my user models :

# user.rb

has_one_attached :picture
has_one_attached :avatar

attr_accessor :remember_token, :activation_token, :crop_x, :crop_y, :crop_w, :crop_h

after_update :crop_picture

def crop_picture
    picture.recreate_versions! if crop_x.present?
end
# mount_uploader :picture, PictureUploader**(No use in ActiveStorage)**
  # mount_uploader :avatar, PictureUploader**(No use in ActiveStorage)**

Part of my user_controllers:

 def new
    @user = User.new
  end

 def create
    @user = User.new(user_params) 
    if @user.save
      log_in @user
      flash[:success] = "Almost success"
      redirect_to confirmation_path(@user.name)
    else
      flash.now[:danger] = 'Failure'
      render :new
    end
  end

def update
    @user = User.friendly.find(params[:id])
    if @user.update_attributes(user_params)
      if params[:user][:picture].present?
        render :crop
      else
        redirect_to @user
        flash[:success] = "Update success"
      end
    else
      flash[:warning] = "Update failue"
      render :edit
    end
  end

private

  def user_params
    params.require(:user).permit(... :avatar, :picture)
  end

My crop.html.erb:

<div class="container">
  <div class="col-md-12 pbt20 bgCo">
    <div class="col-md-8">
      <h4 class="pageItemTitle mbt20">Crop Picture</h4>
      <%#= image_tag @user.picture.url(:large), :id => "user_preview" %>
      <%= image_tag(@user.picture.variant(combine_options: { gravity: 'Center', crop: '600x800+0+0' }),width: 600,id: "cropImage")  %>
    </div>
    <div class="col-md-4">
      <h4 class="pageItemTitle mbt20">Crop Preview</h4>
      <div style="width:160px; height:160px; overflow:hidden; border-radius: 50%;">
        <%= image_tag @user.picture, :id => "user_preview" %>
      </div>
      <div class="crop-footer pull-right mbt20 pbt20">
        <%= form_for @user do |f| %>
          <% %w[x y w h].each do |attribute| %>
            <%= f.hidden_field "crop_#{attribute}" %>
          <% end %>
          <%= link_to "Close", @user,  class: "btn btn-default mr10" %>
          <%= f.submit "Crop", class: "btn btn-primary" %>
        <% end %>
      </div>
    </div>

  </div>
</div>

My crop.js.coffee:

jQuery ->
  new PictureCropper()

class PictureCropper
  constructor: ->
    $('#cropImage').Jcrop
      aspectRatio: 1
      setSelect: [0, 0, 400, 400]
      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) =>
    $('#user_preview').css
      width: Math.round(160/coords.w * $('#cropImage').width()) + 'px'
      height: Math.round(160/coords.h * $('#cropImage').height()) + 'px'
      marginLeft: '-' + Math.round(160/coords.w * coords.x) + 'px'
      marginTop: '-' + Math.round(160/coords.h * coords.y) + 'px'

My picture.uploader:(No use in ActiveStorage)

class PictureUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick
  storage :file

  def store_dir
    "uploaders/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  version :large do
    process :crop
    resize_to_limit(600, nil)
  end

  def crop
    if model.crop_x.present?
      resize_to_limit(600, nil)
      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
        img.crop([[w, h].join('x'),[x, y].join('+')].join('+'))
      end
    end
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end

  def filename
    s = "#{model.id}"
    t = s.to_i * 365
    t.to_s + ".jpg" if original_filename
  end

end

If I chang the upload method from carrierwave to ActiveStorage and at this time I also want to free clipping picture by user. Then how could I chang my codes? Thanks so much!!

SylorHuang
  • 313
  • 4
  • 12

1 Answers1

2

I am storing crop coordinates as a json field named crop_settings in PostgreSQL.

Below is the working code to generate thumbnail variant and cropped image variant.

# Event.rb

class Event < ApplicationRecord
  attr_accessor :crop_x, :crop_y, :crop_w, :crop_h

  has_one_attached :cover_image
  before_save :check_cropping 

  def check_cropping
    self.crop_settings = {x: crop_x, y: crop_y, w: crop_w, h: crop_h} if cropping?
  end 

  def cropping?
    !crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank?
  end

  def cropped_image
    if cover_image.attached?
      if crop_settings.is_a? Hash
        dimensions = "#{crop_settings['w']}x#{crop_settings['h']}"
        coord = "#{crop_settings['x']}+#{crop_settings['y']}"
        cover_image.variant(
          crop: "#{dimensions}+#{coord}"
        ).processed
      else
        cover_image
      end
    end
  end

  def thumbnail(size = '100x100')
    if cover_image.attached?
      if crop_settings.is_a? Hash
        dimensions = "#{crop_settings['w']}x#{crop_settings['h']}"
        coord = "#{crop_settings['x']}+#{crop_settings['y']}"
        cover_image.variant(
        crop: "#{dimensions}+#{coord}",
        resize: size
        ).processed
      else
        cover_image.variant(resize: size).processed
      end
    end
  end
end

views/events/show.html.erb

...
<div>
  Original Image:
  <%= image_tag(@event.cover_image) if @event.cover_image.attached? %>
</div>
<div>
  Cropped Image:
  <%= image_tag(@event.cropped_image) if @event.cropped_image %>
</div>
<div>
  Thumbnail:
  <%= image_tag(@event.thumbnail) if @event.thumbnail %>
</div>
 ...

enter image description here

If the variant was previously created and stored, then it will be used instead of recreating.

enter image description here

Dave
  • 4,376
  • 3
  • 24
  • 37
  • Sorry, @Dave, I just moment saw your answer and comment. I had solved this questions by give up the ActiviStorage and instead of used the gem Carrierwave. Thanks for your attention. – SylorHuang Oct 18 '18 at 15:20
  • @Dave is crop_settings a one to one relationship? or how do you handle crop_settings? – Julio Montoya Jan 05 '19 at 19:59
  • @JulioMontoya crop_settings is a db table column with type JSON. – Dave Jan 06 '19 at 07:42