画像を好みの位置でトリミングする / Rails + jQuery UI draggable

Shunsuke Sawada

画像を投稿させるサービスで、ユーザーが推奨サイズ比率ではない画像をアップロードした場合、好みの位置でトリミングさせるやつを考えてみた。TwitterとかFacebookのカバー写真をアップロードした時のアレです。今回はズームとか無しで、縦方向のみの位置調整にしてみました。(画像のwidthは100%)

画像をトリミングして保存するわけでなく、単に画像位置を動かして、表示範囲以外は見えなくする方法を取っています。切り取ってしまうと、他のレイアウトで使う時に困ることもあるので、個人的にはこの方法が好みです。
  

準備

自分はRailsを使っているので、こんな感じ。

Gemfile

1
gem 'jquery-ui-rails'

  

assets/javascripts/application.js

1
2
3
//= require jquery
//= require jquery.ui.widget
//= require jquery.ui.droppable

  

考え方

drag-and-drop

Rails側はこんな感じ。
PostモデルとImageモデルがあって、Postはサムネイル画像を1枚持ちます。
(Post belongs to image)
画像の位置はimage_posでpostsテーブルに保存します。

models/post.rb

1
2
3
4
class Post < ActiveRecord::Base
    belongs_to :image
end

  
見た目の例は、こんなのです。
CSSは適当なので、環境に合わせて変更してください。

views/posts/edit.html.erb

erb
1
2
3
4
5
6
7
8
9
    <div class="image_attach">
        <%= f.hidden_field :image_id, id: "thumb", data: { draggable: true } %>
        <%= f.hidden_field :image_pos %>
        <% if post.image.present? %>
            <%= image_tag "http://workabroad.jp/image.jpg", style: "top: #{post.image_pos}%" %>
        <% end %>
    </div>

  
assets/stylesheets/posts.css.scss

scss
1
2
3
4
5
6
7
8
9
10
11
.image_attach {
  height: 300px;
  overflow: hidden;
  position: relative;
  img {
    width: 100%;
    position: absolute;
    top: 0;
  }
}

  

Javascriptで実装する

使い回しが効くように、外部ファイルにしました。

assets/javascripts/common.js.coffee

coffee
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
class window.Image

    # Draggable
    @enable_drag: ->
        img = $('.image_attach img')
        box = $('.image_attach')
        pos = $('#post_image_pos')

        $('.image_attach img').draggable({
            axis: "y"
            stop: (event, obj) ->
                # Set position
                range = img.height() - box.outerHeight()
                moved = obj.position.top / box.height() * 100 
                # Top max
                if img.position().top > 0
                    img.css('top', '0px')
                    moved = 0
                # Bottom max
                if Math.abs($(this).position().top) > range
                    img.css('top', '-' + range + 'px')
                    moved = -range / box.height() * 100 

                pos.val(moved)
        })

  
assets/javascripts/posts.js.coffee

coffee
1
2
3
4
5
6
7
8
9
10
ready = ->
    # Draggable
    if $('#thumb').data('draggable') == true
        Image.enable_drag()

# For turbolinks
$(document).ready(ready)
$(document).on('page:load', ready)

ドラッグが終了するタイミング(ドロップ)で、f.hidden_field :image_pos の値を変更しているだけです。今回はレスポンシブデザインを想定しているので、保存する値はpxでなくて、%にしました。

あとはViewで画像を表示する時に、image_pos の値を style="top: #{post.image_pos}%" のように挿入すればいいかと。
  

117
Shunsuke Sawada

おすすめの記事

ワーホリ見積りマシーンつくった
29