画像を好みの位置でトリミングする / Rails + jQuery UI draggable
画像を投稿させるサービスで、ユーザーが推奨サイズ比率ではない画像をアップロードした場合、好みの位置でトリミングさせるやつを考えてみた。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
考え方
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}%"
のように挿入すればいいかと。