在Rails Cropper.js中实现图像裁剪功能


概述

让我们使用Cropper.js在Rails中使用修整功能实现图像上传功能。
cropper_demo.gif

源代码如下。
https://github.com/Tak-Iwamoto/jquery-cropper-rails

环境

Rails 5.2.2
Bootstrap 4.3.1
活动存储
mini_magick 4.9.4

活动存储

执行以下命令以使用

active_storage。
rails active_storage:install
rails db:migrate

载入Cropper.js

https://github.com/fengyuanchen/cropperjs
从上面的github中获取cropper.min.js和cropper.min.css,并将它们放置在Rails中。建议将第三方库放在供应商目录中,在供应商中创建资产/ javascripts和资产/样式表目录,然后分别放置cropper.min.js和cropper.min.css。
https://github.com/fengyuanchen/jquery-cropper
另外,从上述github获取jquery-cropper.min.js以使用JQuery并将其放置在Rails中。最后,如下编辑application.js和application.scss。

application.js

1
2
3
4
5
6
7
8
9
10
//= require rails-ujs
//= require jquery
//= require jquery_ujs
//= require cropper.min.js
//= require jquery-cropper.min.js
//= require activestorage
//= require turbolinks
//= require_tree .
//= require popper
//= require bootstrap-sprockets

application.scss

1
2
3
4
*= require_tree .
*= require cropper.min.css
*= require_self
*/

您现在可以使用Cropper.js。

创建模型和控制器

为演示创建用户模型和控制器。

user.rb

1
2
3
4
class User < ApplicationRecord
  attr_accessor :x, :y, :width, :height
  has_one_attached :image
end

users_controller.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    @user.save
    session[:crop_x] = user_params[:x]
    session[:crop_y] = user_params[:y]
    session[:crop_width] = user_params[:width]
    session[:crop_height] = user_params[:height]

    redirect_to user_path @user
  end

  def show
    @user = User.find(params[:id])
  end

  private
    def user_params
      params.require(:user).permit(:image, :x, :y, :width, :height)
    end
end

我在会话中保留了使用Cropper.js进行修剪的结果的参数,并在以后显示图像时使用它,但是我觉得此方法并不简单...使用active_storage处理的图像我不知道如何保存,所以我是用这种方式做的,但是我认为可能还有另一种好方法。

图片上传表格

创建一个用于上传裁剪图像的表格。

new.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<div class="container">
  <div class="row">
    <div class="col-md-6 col-md-offset-3">
      <div class="preview" style='overflow:hidden'>
          <div id="beforeUpload">
            <%= icon("fas fa-4x fw","file-image")%>
          </div>
          <img _src="" id="croppedImage">
      </div>
      <%= form_with model: @user, url: users_path do |f| %>
        <%= f.file_field :image%>
        <%= f.hidden_field :x, id:"dataX"%>
        <%= f.hidden_field :y, id:"dataY"%>
        <%= f.hidden_field :width, id:"dataWidth"%>
        <%= f.hidden_field :height, id:"dataHeight"%>

        <%= f.submit "button", id:"btnUpload", class: 'btn btn-success'%>
      <% end %>
      <div class="modal fade" id="cropModal" role="dialog">
        <div class="modal-dialog">
          <div class="modal-content">
            <div class="modal-body">
              <div class="img-container">
                <img _src="" id="imageModal" alt="picture">
              </div>
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-primary" id="btn-save" data-dismiss="modal">Save</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="preview" style='overflow:hidden'>创建一个字段以显示裁剪的图像。 ..目前,除非指定样式='overflow:hidden',否则可能无法正确显示。
裁剪的图像显示为<img _src="" id="croppedImage">

使用

form_with创建一个表单,使用f.hidden_field将修整结果的参数传递给控制器??,并在显示以下show.html.erb时使用它。

<div class="modal fade" id="cropModal" role="dialog">下,引导程序显示修整模式。

创建以下html以显示裁剪的图像。
show.html.erb<br>
<%= image_tag @user.image.variant(crop: "#{session[:crop_width]}x#{session[:crop_height]}+#{session[:crop_x]}+#{session[:crop_y]}") %><br>

现在,创建以下javascript文件以执行修剪过程。

crop_image.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
$(function () {
  let $image = $('#imageModal'),
    $img_field = $('#user_image'),
    $croppedImage = $('#croppedImage'),
    $cropModal = $('#cropModal'),
    $beforeUpload = $('#beforeUpload'),
    $button = $('#btn-save'),
    $dataX = $('#dataX'),
    $dataY = $('#dataY'),
    $dataWidth = $('#dataWidth'),
    $dataHeight = $('#dataHeight');

  let options = {
    dragmode: 'crop',
    aspectRatio: 1/1,
    restore: false,
    guides: false,
    center: false,
    highlight: true,
    cropBoxMovable: true,
    cropBoxResizable: true,
    modal: true,
    crop: (e) => {
      $dataX.val(Math.round(e.detail.x));
      $dataY.val(Math.round(e.detail.y));
      $dataWidth.val(Math.round(e.detail.width));
      $dataHeight.val(Math.round(e.detail.height));
    }
  };

  // when file upload
  $img_field.change((e) => {
    $image.cropper('destroy').removeAttr('src');
    file = e.target.files[0];
    reader = new FileReader();
    if (file.type.indexOf('image') < 0) {
      window.alert("画像を選択してください");
      return ;
    }
    reader.onload = ((e) => {
      $image.attr('src',"");
      $image.attr('src', e.target.result);
      $cropModal.modal('show');
      $cropModal.on('shown.bs.modal', () => {
        $image.cropper(options);
      });
    });
    reader.readAsDataURL(file);
  })
  // onclick save button
  $button.click(() => {
    imgCropping();
  });

  // modalを閉じたとき、cropper要素を初期化
  $cropModal.on('hidden.bs.modal',function() {
    $image.cropper('destroy').removeAttr('src');
    let $cropperContainer = $('.cropper-container');
    $cropperContainer.remove();
  });

  function imgCropping() {
    if (!croppable) {
      alert('トリミングする画像が用意されていません')
      return false;
    }
    $beforeUpload.hide();
    let croppedData = $image.cropper('getCroppedCanvas').toDataURL();
    $croppedImage.attr('src', croppedData);
    $cropModal.modal('hide');
  }
});

$ img_field指定ID为user_image的DOM,但是在html端没有这样的ID。请注意,当您使用form_with创建file_field时,Rails会自动创建一个<img id="モデル名_フィールド名">标签。我也上瘾了...

选项设置如何裁剪图像。
$img_field.change描述了当下面上传图像时开始修剪。
显示模态时,使用在$image.cropper(options)前面设置的选项创建裁剪器实例并修剪它。还要注意,我们首先使用$image.cropper('destroy')初始化了裁剪器实例。如果不执行此过程,则先前裁切的图像将保留并显示多幅图像。

另外,当创建裁剪实例时,将创建<div class="cropper-container cropper-bg"></div> DOM。如果在关闭模态时未删除此DOM,则先前裁剪的图像可能会保留。因此,删除操作在$cropModal.on('hidden.bs.modal'下执行。

这就是

的全部。