アプリなどを開発するブログ

React Native / Swift / Ruby on Railsなどの学習メモ。


Paperclip + Jcrop + Railsでサムネイルを正方形に切り抜き

Paperclipで画像アップロードができるアプリケーションを作る際に、
サムネイルを正方形に切り抜きたい場合があります。

Paperclipは勝手にサムネイルを正方形に切り抜いてくれる機能がついてますが、
写真の中心を切り抜く、というものなので、
ユーザーに任意の範囲を切り抜いてもらうにはちと工夫が必要です。

切り抜き範囲をユーザーに指定してもらうために、
JSライブラリのJcropを使用します。

Jcrop

Railscastにわかりやすいチュートリアルがありました。

#182 Cropping Images

http://railscasts.com/episodes/182-cropping-images?view=comments

が、数年前のものなのでこのままだとRails4ではちゃんと動きません。
ちゃんと動いたコードを載せておきます。

環境

  • Rails 4.0.1
  • Paperclip 4.1.1
  • Jcrop 0.9.12

models/User.rb

class User < ActiveRecord::Base
  # 保存する画像ファイル用のフィールドを定義 
  # 保存時にsmall(50x50サイズに切り抜き)、large(500x500以下のサイズに等倍縮小) の画像を自動で作成
  # 保存時のプロセッサを指定
  has_attached_file :photo, :styles => { small: "50x50#", large: "500x500>" }, processors: [:cropper]

  # ユーザーの指定した切り抜き座標保持用プロパティ
  attr_accessor :crop_x, :crop_y, :crop_w, :crop_h

  # 座標を指定されたら、プロセッサを走らせる
  after_update :reprocess_photo, :if => :cropping?

  # 画像のバリデーション。 5メガ以下のファイルのみOK
  validates_attachment_size :photo, less_than: 5.megabytes
  validates_attachment_content_type :photo, content_type:['image/jpeg', 'image/jpg', 'image/png', 'image/gif']

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

  private

  def reprocess_photo
    couple_photo.assign(couple_photo)
    couple_photo.save
  end
end

lib/paperclip_processors/cropper.rb

transformation_commandをオーバーライドする。

module Paperclip
  class Cropper < Thumbnail
    def transformation_command
      if crop_command
        # super returns an array like this: ["-resize", "100x", "-crop", "100x100+0+0", "+repage"]
        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
class UsersController < ApplicationController

  def edit_photo
    @user = current_user
  end

  def update_photo
    @user = current_user
    if @user.update(photo_params)
      if params[:user][:photo].present?
        render action: 'crop'
      else
        redirect_to user_root_path
      end
    end
  end

  def photo_params
    params[:user].permit(:photo, :crop_x, :crop_y, :crop_h, :crop_w)
  end
end