はじめに
ユーザーがアップロードする画像が縦長と横長がバラバラのため、画像一覧が美しく並ばない問題がありました。
その問題を解決するために、縦長と横長専用のCSSクラス名を画像に与え、画像を正方形にマスクする機能を追加しました。
その実装手順を記していきます。
前提/実装環境
- 開発環境はDockerを使用
- Ruby:2.6.3のDockerイメージを使用
- Ruby on Rails:6.0.3
- 「CarrierWave:2.1.0」「MiniMagick:4.10.1」のgemを使用
- 「CarrierWave:2.1.0」は画像をそのままのサイズでアップロードできるところまでは実装済み
仕様
- 画像アップロード時に「CarrierWave」gemで3つのサイズに画像をリサイズ
- 通常サイズ:1024*1024
- ミディアムサイズ:512*512
- スモールサイズ:128*128
- 「MiniMagick」gemを使用し、画像のサイズを取得、縦長・横長画像専用のクラスを与える
- CSSでマスクをかけて正方形に描画するようにする
実装手順
【1】画像アップロード時に「CarrierWave」gemで3つのサイズに画像をリサイズ
まず、画像アップロード時に「CarrierWave」gemで3つのサイズに画像をリサイズできるようにしていきます。
リサイズできるようにするために下記の作業を行います。
- 「Imagemagick」をインストール
- 「MiniMagick」gemをインストール
- リサイズの設定を「CarrierWave」gemの設定ファイルで行う
「CarrierWave」でリサイズを行うためには、「MiniMagick」gemのインストールが必要で、
「MiniMagick」gemを使用できるようにするためには、「Imagemagick」というソフトウェアのインストールが必要なため、
上記の作業を行います。
下記のコードの引用にあるように、「CarrierWave」のuploader設定ファイルにある、
「MiniMagick」gemファイルをincludeするコードのコメントアウトを外することで、
画像のリサイズができるようになります。
app/uploaders/****_uploader.rbclass 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
DockerfileでImagemagickをインストール
私は開発環境にDockerを使用しているので、下記のようにrubyイメージの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」をインストール
gem "mini_magick"
$ bunndle install
リサイズの設定をcarrierwaveの設定ファイルで行う
私のアップローダー名はimageですので、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のリポジトリに説明がありましたので、そちらを参照してください。
【2】 「MiniMagick」gemを使用し、画像のサイズを取得、縦長・横長画像専用のクラスを与える
今回は画像を様々な箇所で画像を正方形で出したかったので、画像の取得を、メソッドの引数でmedium_thumb
、small_thumb
などのように入力することで、画像を取得し分けられるようにし、
HTML出力部分はパーツ化させて汎用性をもたせるようにしました。
よって下記の作業を行いました。
- 画像の取得を、メソッドの引数で
medium_thumb
、small_thumb
などのように入力することで、画像を取得し分けられるようにする - パーツ化させたhtml.erbファイルを作成
画像の取得を、メソッドの引数でmedium_thumb
、small_thumb
などのように入力することで、画像を取得し分けられるようにする
コールバック関数の機能を利用し、引数で画像を取得できるようにコードを作成します。
既にアプリをデプロイして利用しているユーザーが存在するため、リサイズされた画像が存在しないときは、オリジナル画像を出力するようにしています。
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リポジトリ
<%
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画像を取得するようにしています。
上記のパーツを使用して画像を呼び出すためには、下記のようにして画像を呼び出します。
<%= render partial: 'layouts/img-square', locals: { image: current_user.image, version: "small_thumb" } %>
【3】CSSでマスクをかけて正方形に描画するようにする
最後は、SCSSファイルで画像を縦・横それぞれのクラス名に合わせてマスクするように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;
}
}
}
完成物
わからなかった箇所・課題
- 今後、
application_controller.rb
にメソッドを追加していくと、コードが増えることにより、カオスになっていくことが予想されるため、コードを整理できるようにしていく必要がありそう。。。 - オリジナル画像は削除できるようにできるとさらにGood?オリジナル画像は容量が大きいこともあるため、サーバー容量が圧迫していくことが予想されるため
さいごに
わからない箇所がありますが、なんとか記事一覧を正方形で並べるところまで実装できたのでよかったです。
画像サイズは、Ruby専用メソッドで取得ができるのかなと思ったのですが、実際調べると無かったので、「MiniMagick」gemがあって良かった。