carrierwave + swiper(v6.4.5) + Rails で複数枚の画像のスライド機能実装

環境

・Rails5.2.6
・Ruby2.7.1
・swiper6.4.5

ActiveStorage と CarrierWave [比較]

ActiveStorage

  • メリット
    • erbを使えばcontrollerのコード記述を少なく書ける
  • デメリット
    • データ構造がわかりにくい

CarrierWave

  • メリット
    • CarrierWave::Uploaderが作られ、ここに画像の処理するコードを記載すれば良いのでActiveStorageに比べてわかりやすい
  • デメリット
    • 特になし


下記の記事が参考になりました
ActiveStorage vs CarrierWave

実装

gem 'carrierwave'をGemfileに加える

$ bundle installでインストール

$ rails g uploader image (imageの部分は好きな名前で) app/uploaders/image_uploader.rbファイルが作られる

$ rails g migration add_images_to_posts images:jsonでデータベースにカラムを追加する (複数枚の画像だからimages)

$ rails db:migrate

モデルファイルを編集(今回はpost.rb)

post.rb

mount_uploaders :images, ImageUploader   # 複数枚だから uploaders、images


コントローラーを編集
ストロングパラメーターに{ images[] }を追加し、カラムに配列で保存出来る様にする。

def post_params
  params.require(:post).permit(:body, { images: [] })
end
# 複数枚だからカラムに配列で保存出来る様にする


画像投稿フォームを作成

.form-group
          = f.label :images, '画像', class: 'bmd-label-floating'
          = f.file_field :images, multiple: true, class: 'form-control'
# 複数枚投稿できるように multiple: true 入れる


画像を表示させる

- @post.images each do |image| 
  = image_tag image.url



☆ mini_magickを使って画像のサイズをアップロード時に変更出来る様にする

前提:$ brew install imagemagickimagemagickをPCにインストール

gem 'mini_magick'をGemfileに追加
$ bundle install
image_uploader.rbを編集。include CarrierWave::MiniMagickを追記し、その下にサイズ設定を記述する

image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base 
  include CarrierWave::MiniMagick
  process resize_to_fill: [400, 400, 'Center']
# 縦横比を維持せずに、元画像から指定サイズ(400, 400)で指定位置('Center')から切り抜きを行う
end

Swiper

※swiperのバージョン6.4.5を使っています。バージョン7はいろいろ変わっているので注意!

swiperとは

JavascriptCssを使って画像スライド機能を実装するためのライブラリ(インスタグラムにあるような画像スワイプ機能の実装を手伝ってくれる)

swiperの導入方法(2つ紹介)


方法①: CDN(Content Delivery Network)を使う。CDNを使えばクラウド上にSwiperが公開しているCSSやJSを引っ張ってこれる

application.html.erbに書き込む

<head>

<link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.css">
<script src="https://unpkg.com/swiper/swiper-bundle.js"></script>

</head>



方法② 『yarn』や『npm』というNode.js(Javascript)のパッケージマネージャを使う(npmよりyarnの方が優秀らしい)
パッケージマネージャとは、ライブラリ同士の依存関係を解消してくれたりバージョンを管理してくれたりするツール(ちなみに、Railsのgemたちのパッケージマネージャはbundler)

yarnを使った導入方法

1st) Swiperのインストール 

前提:$ brew install yarnでPCにyarnをインストール ($ brew lsでyarnが既に入っているか確認できる)

$ cd アプリ名

$ yarn init
# package.jsonが作られる(railsでいうと、$ bundle initで空のGemfileが生成されるような感じ)

$ yarn add swiper
# package.jsonに『"swiper": "6.4.5"』みたいに記載される
(よく考えたらGemfileの場合でも『gem 'bootsnap', '>= 1.1.0'』みたいに書く)

$  yarn install
# swiperがインストールされる。つまり必要なファイルが、アプリ名/app/node_modeles/ディレクトリ配下に作られる(下記画像)   
(gemの場合でも$ bundle installで特定のディレクトリ配下にgemの処理が書かれたファイルが作られる。$ gem environment でどのディレクトリにファイルが作られたのか確認できる)

補足
 $ yarn upgrade swiper@6.4.5でバージョン入れ替えられる。 
 yarnでインストールしたライブラリは $ yarn listで確認できる

Image from Gyazo


2nd)インストールしたswiperのファイルたちを読み込ませる
assets/javascript/application.js(マニフェストファイル)に書き込む

//= require swiper/swiper-bundle.js
//= require swiper.js

# 上段は、node_modules/swiper/以下のファイルを読み込む(node_modulesは省略できる)
# 下段は、assets/javascript/application.jsと同一ディレクトリに存在するswiper.jsを読み込む(swiper.jsは後で自分で作る)
assets/stylesheets/application.scssにも書き込む

@import 'swiper/swiper-bundle';
config/initializers/assets.rbに書き込む

Rails.application.config.assets.paths << Rails.root.join('node_modules')
# 導入したnode_modules以下のファイルを読み込むようにするための設定


3rd)実装

新しいjavascriptファイル(今回はswiper.jsと名付ける)をapp/assets/javascripts配下に作る

app/assets/javascripts/swiper.jsにjsの処理を書く(公式ドキュメントを参考に)

// ここがポイント!!!!
// アセットパイプラインにおけるJSについてはES6記法であるrequireは使えない
// また、DOMの読み込みを待ってあげる必要があるので $(functionで囲う必要がある

 $(function() {
  var mySwiper = new Swiper('.swiper-container', {
    // Optional parameters
    direction: 'horizontal',
    loop: true,

    // If we need pagination
    pagination: {
      el: '.swiper-pagination',
    },

    // Navigation arrows
    navigation: {
      nextEl: '.swiper-button-next',
      prevEl: '.swiper-button-prev',
    },

    // And if we need scrollbar
    scrollbar: {
      el: '.swiper-scrollbar',
    },
  })  
 })

$(function() {})は予約機能みたいなもの。swiper.jsがhtmlのbodyタグよりも先に読み込まれた場合でも、htmlにswiper.jsの中身($(function() {})内の記述)を適切なタイミングで反映させてくれる。
そのため、$(function() {})を書かないと、htmlにswiper.js内のjavascriptが反映されないという現象が起こる。

application.html.erb

<html>
  <head>

    <%= stylesheet_link_tag 'application', media: 'all' %>
    <%= javascript_pack_tag 'application' %> <!-- ここで application.jsが読み込まれる(つまりswiper.jsなどが読み込まれる) -->
   
  </head>

  <body>
     <%= yield %> <!-- application.jsが読み込まれた後でここが読み込まれる⇨$(function() {})が必要。-->
  </body>


swiperを反映させたい◯◯.html.erbに書き込む

参考にするhtml

<div class="swiper-container">
  <div class="swiper-wrapper">

    <div class="swiper-slide">
      <div class="slide-img"><img src="images/img_01.jpg" alt=""></div>
      <div class="slide-content">テスト1</div>
    </div>

    <div class="swiper-slide">
      <div class="slide-img"><img src="images/img_02.jpg" alt=""></div>
      <div class="slide-content">テスト2 </div>
    </div>

    <div class="swiper-slide">
      <div class="slide-img"><img src="images/img_03.jpg" alt=""></div>
      <div class="slide-content">テスト3</div>
    </div>

  </div> <!-- swiper-wrapperの終わり -->

  <div class="swiper-pagination swiper-pagination-white"></div>
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>

</div> <!-- swiper-containerの終わり -->


今回はshow.html.erbに反映させる

<div class="c-image-container">
      <div class="card-img-top.swiper-container">
        <div class="swiper-wrapper">
          <% if @post.images.present? %>
            <% @post.images.each do |image| %>
              <%= image_tag image.url,size: '400x300', class: 'swiper-slide', alt: '@post iamge' %>
            <% end %>
          <% end %>
      </div>
       <% if @post.images.count >= 2 %>
          <div class="swiper-pagination"></div>
          <div class="swiper-button-prev"></div>
          <div class="swiper-button-next"></div>
       <% end %>
</div>


場合によってはapplication.scssにcssを書き加える(公式ドキュメント参照)



参考記事

RailsでSwiperを導入する方法(Swiperは2020年7月にバージョンアップし、従来と設定方法が変わりました!)

swiperをyarnで導入して、画像をスライダー形式にする!