【Rails/CarrierWave/MiniMagick】縦長と横長専用のCSSクラス名をアップロードした画像のimgタグに与える

# はじめに

ユーザーがアップロードする画像が縦長と横長がバラバラのため、画像一覧が美しく並ばない問題がありました。

![スクリーンショット 2020-12-29 18.48.37.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261027/ef87c00c-dbc5-c77d-b62a-75b82e71575d.jpeg)


その問題を解決するために、縦長と横長専用のCSSクラス名を画像に与え、画像を正方形にマスクする機能を追加しました。

その実装手順を記していきます。

# 前提/実装環境

- 開発環境はDockerを使用
- Ruby:2.6.3のDockerイメージを使用
- Ruby on Rails:6.0.3
- 「[CarrierWave:2.1.0](https://github.com/carrierwaveuploader/carrierwave)」「[MiniMagick:4.10.1](https://github.com/minimagick/minimagick)」のgemを使用
- 「[CarrierWave:2.1.0](https://github.com/carrierwaveuploader/carrierwave)」は画像をそのままのサイズでアップロードできるところまでは実装済み

# 仕様

1. 画像アップロード時に「CarrierWave」gemで3つのサイズに画像をリサイズ
    1. 通常サイズ：1024*1024
    2. ミディアムサイズ：512*512
    3. スモールサイズ：128*128
2. 「MiniMagick」gemを使用し、画像のサイズを取得、縦長・横長画像専用のクラスを与える
3. CSSでマスクをかけて正方形に描画するようにする


# 実装手順

## 【1】画像アップロード時に「CarrierWave」gemで3つのサイズに画像をリサイズ

まず、画像アップロード時に「CarrierWave」gemで3つのサイズに画像をリサイズできるようにしていきます。
リサイズできるようにするために下記の作業を行います。

- 「Imagemagick」をインストール
- 「MiniMagick」gemをインストール
- リサイズの設定を「CarrierWave」gemの設定ファイルで行う

「CarrierWave」でリサイズを行うためには、「MiniMagick」gemのインストールが必要で、
「MiniMagick」gemを使用できるようにするためには、「Imagemagick」というソフトウェアのインストールが必要なため、
上記の作業を行います。


下記のコードの引用にあるように、「CarrierWave」のuploader設定ファイルにある、
「MiniMagick」gemファイルをincludeするコードのコメントアウトを外することで、
画像のリサイズができるようになります。


>```ruby:app/uploaders/****_uploader.rb
>class MyUploader < CarrierWave::Uploader::Base
>  include CarrierWave::MiniMagick #<= コメントアウトされた「MiniMagick」gemを外することで、resizeができるようになる
>
>  process resize_to_fit: [800, 800]
>
>  version :thumb do
>    process resize_to_fill: [200,200]
>  end
>
>end
>```


下記の引用にあるように、「MiniMagick」gemファイルで画像をリサイズするためには、
「Imagemagick」のインストールが必要とのことです。


> Note: You must have Imagemagick installed to do image resizing.
> [https://github.com/carrierwaveuploader/carrierwave](https://github.com/carrierwaveuploader/carrierwave)



### DockerfileでImagemagickをインストール

私は開発環境にDockerを使用しているので、下記のようにrubyイメージのDockerfileにインストールコマンドを追加しました。

```Dockerfile:docker/rails/Dockerfile

FROM ruby:2.6.3

# 省略

RUN apt-get update && apt-get install -y --no-install-recommends\
  nodejs \
  default-mysql-client \
  vim \
  yarn \
  imagemagick \ #<= 追加
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

# 省略

```


### gemで「MiniMagick」をインストール

```Gemfile:Gemfile
gem "mini_magick"
```

```terminal
$ bunndle install
```


### リサイズの設定をcarrierwaveの設定ファイルで行う

私のアップローダー名はimageですので、```image_uploader.rb```ファイルを下記のように編集します。

```ruby:app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick #<= コメントアウトを外す

  # 省略

  # Create different versions of your uploaded files:
  version :thumb do
    process resize_to_fit: [1024, 1024]
  end

  version :medium_thumb, from_version: :thumb do
    process resize_to_fit: [512, 512]
  end

  version :small_thumb, from_version: :medium_thumb do
    process resize_to_fit: [128, 128]
  end

  # 省略

end
```

Railsアプリを再起動すると、画像アップロード時に画像がリサイズされるようになっていました。

リサイズのやり方は様々あったのですが、上記は、画像の縦横そのままの比率でリサイズする「resize_to_fit」という処理を使用しました。
その他のリサイズ方法は[GitHuのMiniMagickのリポジトリ](https://github.com/minimagick/minimagick)に説明がありましたので、そちらを参照してください。


## 【2】 「MiniMagick」gemを使用し、画像のサイズを取得、縦長・横長画像専用のクラスを与える

今回は画像を様々な箇所で画像を正方形で出したかったので、画像の取得を、メソッドの引数で```medium_thumb```、```small_thumb```などのように入力することで、画像を取得し分けられるようにし、
HTML出力部分はパーツ化させて汎用性をもたせるようにしました。

よって下記の作業を行いました。

- 画像の取得を、メソッドの引数で```medium_thumb```、```small_thumb```などのように入力することで、画像を取得し分けられるようにする
- パーツ化させたhtml.erbファイルを作成


### 画像の取得を、メソッドの引数で```medium_thumb```、```small_thumb```などのように入力することで、画像を取得し分けられるようにする


コールバック関数の機能を利用し、引数で画像を取得できるようにコードを作成します。
既にアプリをデプロイして利用しているユーザーが存在するため、リサイズされた画像が存在しないときは、オリジナル画像を出力するようにしています。


```ruby:app/controllers/application_controller.rb

class ApplicationController < ActionController::Base


  protected

    # Call the method to get the uploaded image
    def get_uploaded_image(image, method = "thumb")
      self.send(method.to_sym, image)
    end
    helper_method :get_uploaded_image

    # Returns the value of the src attribute of
    # the img tag of the thumbnail size image.
    def thumb(image)
      unless image.thumb.blank?
        return image.thumb.url.to_s
      end

      image.url.to_s
    end

    # Returns the value of the src attribute of
    # the img tag for a medium-sized thumbnail image.
    def medium_thumb(image)
      unless image.thumb.blank?
        return image.medium_thumb.url.to_s
      end

      image.url.to_s
    end

    # Returns the value of the src attribute
    # of the img tag for a small-sized thumbnail image.
    def small_thumb(image)
      unless image.thumb.blank?
        return image.small_thumb.url.to_s
      end

      image.url.to_s
    end
end

```


### パーツ化させたhtml.erbファイルを作成

下記のファイルで、縦長か、横長かを「MiniMagick」のメソッドで判定して、クラス名を与えます。
参考：[MiniMagickのGitHubリポジトリ](https://github.com/minimagick/minimagick)

```html.erb:app/views/layouts/_img-square.html.erb
<%
unless image.blank?
  image_url = get_uploaded_image(image, version)
  image_obj = MiniMagick::Image.open("#{Rails.root.to_s}/public#{image_url}")
  class_name = image_obj.width >= image_obj.height ? "img-square__width":"img-square__height"
  img_width = image_obj.width
  img_height = image_obj.height
else
  image_url = "nophoto.jpg"
  class_name = "img-square__width"
  img_width = 600
  img_height = 600
end
%>
<div class="img-square"><%= image_tag image_url, class: class_name, width: img_width, height: img_height %></div>

```

取得する画像が存在しない場合は、nophoto画像を取得するようにしています。

上記のパーツを使用して画像を呼び出すためには、下記のようにして画像を呼び出します。

```html.erb
<%= render partial: 'layouts/img-square', locals: { image: current_user.image, version: "small_thumb" } %>
```


## 【3】CSSでマスクをかけて正方形に描画するようにする

最後は、SCSSファイルで画像を縦・横それぞれのクラス名に合わせてマスクするようにSCSSのコードを書いていきます。

```scss:app/assets/stylesheets/application.scss

.img-square {
  width: 100%;
  padding-top: 50%;
  padding-bottom: 50%;
  position: relative;

  @at-root {
    #{&}__height {
      width: 100%;
      height: auto;
      position: absolute;
      top: -50%;
      bottom: -50%;
      right: -100%;
      left: -100%;
      margin: auto;
    }

    #{&}__width {
      height: 100%;
      width: auto;
      max-width: none;
      position: absolute;
      top: -100%;
      bottom: -100%;
      right: -100%;
      left: -100%;
      margin: auto;
    }
  }
}

```

## 完成物

![スクリーンショット 2020-12-29 18.48.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261027/2e9dec35-cdd5-ac37-908d-838c416e416b.png)



# わからなかった箇所・課題

- 今後、```application_controller.rb```にメソッドを追加していくと、コードが増えることにより、カオスになっていくことが予想されるため、コードを整理できるようにしていく必要がありそう。。。
- オリジナル画像は削除できるようにできるとさらにGood?オリジナル画像は容量が大きいこともあるため、サーバー容量が圧迫していくことが予想されるため

# さいごに
わからない箇所がありますが、なんとか記事一覧を正方形で並べるところまで実装できたのでよかったです。
画像サイズは、Ruby専用メソッドで取得ができるのかなと思ったのですが、実際調べると無かったので、「MiniMagick」gemがあって良かった。