[Rails/CarrierWave/MiniMagick] Give the CSS class name for portrait and landscape to the img tag of the uploaded image.

Introduction

There was a problem that the image list did not line up beautifully because the images uploaded by the user were different in portrait and landscape.

スクリーンショット 2020-12-29 18.48.37.jpeg

To solve that problem, we added a function to mask the image to a square by giving the image a CSS class name dedicated to portrait and landscape.

I will describe the implementation procedure.

Prerequisite/implementation environment

--Development environment uses Docker --Ruby: Uses 2.6.3 Docker image

specification

  1. Resize the image to 3 sizes with the "Carrier Wave" gem when uploading the image
  2. Normal size: 1024 * 1024
  3. Medium size: 512 * 512
  4. Small size: 128 * 128
  5. Use the "MiniMagick" gem to get the size of the image and give it a class dedicated to portrait and landscape images
  6. Mask with CSS to draw in a square

Implementation procedure

[1] Resize the image to 3 sizes with the "Carrier Wave" gem when uploading the image

First, when uploading an image, we will make it possible to resize the image to three sizes with the "Carrier Wave" gem. To be able to resize, do the following:

--Install "Imagemagick" --Install the "Mini Magick" gem --Set the resizing in the "Carrier Wave" gem configuration file

In order to resize with "Carrier Wave", you need to install "MiniMagick" gem. In order to be able to use the "MiniMagick" gem, it is necessary to install software called "Imagemagick". Do the above work.

As you can see in the code quote below, in the "Carrier Wave" uploader config file, By uncommenting the code that includes the "MiniMagick" gem file You will be able to resize the image.

class MyUploader < CarrierWave::Uploader::Base
 include CarrierWave::MiniMagick #<=You can resize by removing the commented out "MiniMagick" gem.

 process resize_to_fit: [800, 800]

 version :thumb do
   process resize_to_fill: [200,200]
 end

end

To resize the image in the "MiniMagick" gem file, as in the quote below It is necessary to install "Imagemagick".

Note: You must have Imagemagick installed to do image resizing. https://github.com/carrierwaveuploader/carrierwave

Install Imagemagick with Dockerfile

Since I am using Docker for the development environment, I added the installation command to the Dockerfile of the ruby ​​image as shown below.

docker/rails/Dockerfile



FROM ruby:2.6.3

#abridgement

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

#abridgement

Install "Mini Magic" with gem

Gemfile


gem "mini_magick"
$ bunndle install

Set the resizing in the carrierwave configuration file

My uploader name is image, so edit the `` `image_uploader.rb``` file as follows.

app/uploaders/image_uploader.rb


class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick #<=Uncomment

  #abridgement

  # 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

  #abridgement

end

When I restarted the Rails app, the image was resized when I uploaded the image.

There were various ways to resize, but the above used a process called "resize_to_fit" that resizes the image at the same aspect ratio. Other resizing methods are explained in GitHu's MiniMagick repository, so please refer to that.

[2] Use the "MiniMagick" gem to get the size of the image and give it a class dedicated to portrait and landscape images.

This time, I wanted to display the image as a square in various places, so by inputting the image acquisition as `medium_thumb```, `small_thumb```, etc. in the method argument, Make it possible to acquire and separate images The HTML output part has been made into parts to give it versatility.

Therefore, the following work was done.

--By inputting the image acquisition as `medium_thumb```, `small_thumb```, etc. in the method argument, the image can be acquired and separated. --Create a partized html.erb file

By inputting the image acquisition as `medium_thumb```, `small_thumb```, etc. in the method argument, the image can be acquired and separated.

Use the function of the callback function and write the code so that you can get the image with the argument. Since there are users who have already deployed and used the app, when the resized image does not exist, the original image is output.

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

Create a partized html.erb file

In the file below, determine whether it is portrait or landscape with the "MiniMagick" method, and give the class name. Reference: MiniMagick's GitHub repository

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>

If the image to be acquired does not exist, I try to acquire the nophoto image.

To call an image using the above parts, call the image as follows.

html.erb


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

[3] Mask with CSS and draw in a square

Finally, write the SCSS code so that the image is masked according to the vertical and horizontal class names in the SCSS file.

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;
    }
  }
}

Finished product

スクリーンショット 2020-12-29 18.48.44.png

Parts / issues I did not understand

--In the future, if you add methods to application_controller.rb, it is expected that the code will increase and become chaos, so it is necessary to be able to organize the code. so. .. .. --It would be even better if the original image could be deleted? Since the original image may have a large capacity, it is expected that the server capacity will be squeezed.

at the end

There are some things I don't understand, but I'm glad that I managed to implement the article list in a square. I wondered if the image size could be obtained with a Ruby-only method, but I didn't actually check it, so I'm glad I had the "MiniMagick" gem.

Recommended Posts

[Rails/CarrierWave/MiniMagick] Give the CSS class name for portrait and landscape to the img tag of the uploaded image.
I want to give a class name to the select attribute
How to check the extension and size of uploaded files
How to set the IP address and host name of CentOS8
[Rails] Articles for beginners to organize and understand the flow of form_with
I want you to use Enum # name () for the Key of SharedPreference
Change the port name and execution authority of the microcomputer connected to Ubuntu and LiDAR
Get the class name and method name of Controller executed by HandlerInterceptor of Spring Boot
I want to recursively get the superclass and interface of a certain class