LoginSignup
14
12

More than 5 years have passed since last update.

rails 4.2, paperclip, Jcropを使ってユーザが写真切り取りし保存する機能をつける

Last updated at Posted at 2015-11-14

はじめに

大体探しつくのは、以下のものである

#182 Cropping Images
#182 Cropping Images (revised)

問題は、これらはとても使えるがすでに5年前のために、古くてRails4でそのまま使うことができない。

作者もこのままで挑戦したが、Infinite loopが起こった。

以下の様なStack Overflowにあたることになる。
paperclip jcrop and rails 4 infinite loop fix

今回さ作者が、いろいろ調べ、最終的にできるようになるまで時間がかかったので、ここに書いて他の人の参考になればと思い、記す。

基本的に、上記のコードをそのまま使うが、一部変更がある。流れを理解するために、上のビデオを見ることを奨励する。

前提

すでにPaperclipで画像を保存できる状態だとする。

今回作者の条件は、Userモデルに、imageという画像ファイルを保存するようになっている。

本題

1. Jcropをダウンロードし、Railsで読み込む

Jcrop » the jQuery Image Cropping Plugin

からダウンロードし、jquery.Jcrop.jsとjquery.Jcrop.min.cssをそれぞれ、vendor/assets/javascripts/と vendor/assets/stylesheets/に置く

これをapp/assets/javascripts/application.jsで以下の様に読み込む

//= require jquery.Jcrop

app/assets/javascripts/application.css

*= require jquery.Jcrop.min

これでjcropを読み込むことができた!

2. cropするテンプレートを作り、そこに飛ばすようにする。

app/views/users/crop.haml

:javascript
  $(function(){
    $("#cropbox").Jcrop({//サイズ変更があったら、アップデートする
      onChange: update_crop,
      onSelect: update_crop,
      setSelect: [0,0,300,300],
      aspectRatio: 1
    });
  });
  function update_crop(coords){//アップデートする関数で、
    var rx = 100/coords.w;
    var ry = 100/coords.h;
    $("#preview").css({//プレビューを更新する。
      width: Math.round(rx * #{ @user.image_geometry(:medium).width }) + 'px',
      height: Math.round(ry * #{ @user.image_geometry(:medium).height }) + 'px',
      marginLeft: '-' + Math.round(rx * coords.x) + 'px',
      marginTop: '-' + Math.round(ry * coords.y) + 'px'
    });
    var ratio = #{@user.image_geometry(:original).width}/#{@user.image_geometry(:medium).width};
    $("#crop_x").val(coords.x * ratio);//クロップするための4つの変数をformの中で更新する
    $("#crop_y").val(coords.y * ratio);//
    $("#crop_w").val(coords.w * ratio);//
    $("#crop_h").val(coords.h * ratio);//
  }
= image_tag @user.image.url(:medium), id: "cropbox"

%h4 プレビュー
%div{style: "width:100px; height:100px; overflow:hidden"}
  = image_tag @user.image.url(:medium), id: "preview"


= form_for @user do |f|
  .form-group
    - for attribute in [:crop_x, :crop_y, :crop_w, :crop_h]
      = f.hidden_field attribute, id: attribute

  %p= f.submit '送信', class: 'btn btn-primary'

users_controller.rbのupdate(createでも)で、画像が更新されるときに、cropに飛ぶようにする。

  def update
    @user = User.find_by(account: params[:id])
    if @user.update_attributes(user_params)
      if params[:user][:image].blank?#画像がないときは、今までどおり更新
        flash[:success] = "更新されました"
        redirect_to @user
      else#画像があるときは、Cropに飛ばす。
        render :crop
      end
    else
      render :edit
    end
  end

3. userモデルで、更新した時に、画像を変更するようにコードを足す

app/models/user.rb

attr_accessor .... , :crop_x, :crop_y, :crop_w, :crop_h
after_update :reprocess_image, if: :cropping?#クロップするための位置情報が送られてきたら、updateの後に、reprocess_imageというPrivateメソッドを呼ぶ

...

has_attached_file :image, styles: {medium: "300x300>", thumb: "100x100#", icon: "40x40#"}, processors: [:cropper]
# mediumを使ってCropするので、”>”を使う。"#"を使うと直接真ん中部分を正方形に切り取って、保存されてしまうため、切り取り作業を行うときに、すでに正方形のものからしか切り取れなくなる!
# processors: [:cropper]は4に書く、paperclipをoverrideするClass名である。(この名前は任意)

def cropping?# クロップするための位置情報が送られてきたかチェック
  crop_x && crop_y && crop_w && crop_h
end

def image_geometry(style=:original)
  @geometry ||= {}
  @geometry[style] ||= Paperclip::Geometry.from_file(image.path(style))
end

...

private

def reprocess_image # ここが、RailsCastと違う!!
  image.assign(image)
  image.save
end

4. 最後に、PaperclipをOverrideする(railscastと異なる部分)

config/initializers/cropper.rbに以下を書く(railscastでは、lib/processors/cropper.rbとしていた。)

module Paperclip
  class Cropper < Thumbnail
    def transformation_command
      if crop_command
        crop_command + super.join(' ').sub(/ -crop \S+/, '').split(" ")
      else
        super
      end
    end

    def crop_command
      target = @attachment.instance
      if target.cropping?
        [" -crop", "#{target.crop_w}x#{target.crop_h}+#{target.crop_x}+#{target.crop_y}"]
      end
    end
  end
end

、paperclipは、以下のようにimagemagickのコマンドを実行するので、上のコードはインスタンス変数@userから形を整えていると思えばいい。

convert input.png -crop 100x100+0+10 output.png

完成!

うまくいかない場合のチェック点。

切り出しのJcropが動いているか?

hidden_fieldとなっている、crop_x, crop_y, crop_w, crop_hをtext_fieldに変えることで、変数がちゃんとうごいているかをチェックできる

overrideしたtransformation_commandが呼ばれているか。

puts "transformation_command"などを入れ、確認。
railscastの指定する場所だと呼ばれないので、これが結構重要

参考

how to allow users to crop images in rails 4

14
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
12