作ったもの
よく、SNSで利用するようなアイコンをアップロードする際、好きな箇所で切り取る処理を作成してみたのでメモがてら書いていく。
こんな感じの動き。
開発環境について
前回作成した下記エントリーの開発環境をそのまま利用。
変更点として、users
Model と employees
Modelの使い方を、思っていたのと間違えていたっぽいので修正。
users
: deviseで利用するアカウント情報として利用employees
: 社員管理
ソース
コチラ に配置しているので、よかったらツッコミおなしゃす。
docker−compose環境があれば、my_strongest_ror/docker/start.sh
を叩くだけで動きます(たぶん)。
準備 - Cropperのダウンロード&配置
Cropper.js から cropper.min.js
と cropper.min.css
をダウンロードして、下記に配置。
$ cd my_strongest_ror/ $ tree . ├── app │ ├── assets │ │ ├── javascripts │ │ │ ├── cropper.min.js │ │ └── stylesheets │ │ └── cropper.min.css
Cropper.js本体を アバター編集画面のみで読み込ませたいので、 my_strongest_ror/config/initializers/assets.rb
に、下記を追記。
Rails.application.config.assets.precompile += %w( *.js # JSはすべてAsset Pipeline対象とする cropper.css # 画像Crop用 )
画面作成
ちょっと適当だけど、下記の様にアバター変更画面を作成。
<h1>アイコン変更画面</h1> <form> <%= hidden_field_tag "employee_id", @employee.id %> <%= file_field_tag 'employee[avatar]' %> <%= button_tag :submit, :id => "submitBtn" %> <% if @employee.persisted? && @employee.avatar? %> <%= link_to "削除", delete_avatar_path, method: :delete %> <% end %> <%= link_to "キャンセル", employees_path %> <% if @employee.persisted? && @employee.avatar? %> <div id="crop_area_box"> <%= image_tag @employee.avatar, :id => "crop_image", :html => [:width => "600px"] %> </div> <% else %> <div id="crop_area_box"><img id="crop_image" /></div> <% end %> <div id="crop_preview"></div> </form> <!-- Cropper.jsは、当該ページ個別に読み込む --> <%= stylesheet_link_tag 'cropper.min' %> <%= javascript_include_tag 'cropper.min' %> <%= javascript_include_tag 'edit_avatar' %> <style> #crop_area_box { display: block; width: 600px; height: 600px; overflow: hidden; } </style>
Cropper.jsを利用するアバター変更画面用のJSを作成する。
$(function(){ var fileName; // 画像ファイル選択後のプレビュー処理 $('form').on('change', 'input[type="file"]', function(event) { var file = event.target.files[0]; fileName = file.name; var reader = new FileReader(); var $crop_area_box = $('#crop_area_box'); // 画像ファイル以外の場合は何もしない if(file.type.indexOf('image') < 0){ return false; } // ファイル読み込みが完了した際のイベント登録 reader.onload = (function(file) { return function(event) { //既存のプレビューを削除 $crop_area_box.empty(); // .prevewの領域の中にロードした画像を表示するimageタグを追加 $crop_area_box.append($('<img>').attr({ src: event.target.result, id: "crop_image", title: file.name })); // プレビュー処理に対して、クロップ出来る処理を初期化設定 initCrop(); }; })(file); reader.readAsDataURL(file); }); var cropper; function initCrop() { cropper = new Cropper(crop_image, { dragMode: 'move', // 画像を動かす設定 aspectRatio: 1 / 1, // 正方形やで! restore: false, guides: false, center: false, highlight: false, cropBoxMovable: false, cropBoxResizable: false, toggleDragModeOnDblclick: false, minCropBoxWidth: 300, minCropBoxHeight: 300, ready: function () { croppable = true; } }); // 初回表示時 crop_image.addEventListener('ready', function(e){ cropping(e); }); // 画像をドラッグした際の処理 crop_image.addEventListener('cropend', function(e){ cropping(e); }); // 画像を拡大・縮小した際の処理 crop_image.addEventListener('zoom', function(e){ cropping(e); }); } // クロップ処理した画像をプレビュー領域に表示 var croppedCanvas; function cropping(e) { croppedCanvas = cropper.getCroppedCanvas({ width: 300, height: 300, }); // `$('<img>'{src: croppedCanvas.toDataURL()});` 的に書きたかったけど、jQuery力が足りず・・・ var croppedImage = document.createElement('img'); croppedImage.src = croppedCanvas.toDataURL(); crop_preview.innerHTML = ''; crop_preview.appendChild(croppedImage); } // Submit時に実行するPOST処理 $('#submitBtn').on('click', function(event){ // クロップ後のファイルをblobに変換し、AjaxでForm送信 croppedCanvas.toBlob(function (blob) { const fileOfBlob = new File([blob], fileName); var formData = new FormData(); // `employee[avatar]` は `employee` modelに定義した `mount_uploader :avatar, AvatarUploader` のコト formData.append('employee[avatar]', fileOfBlob); // EmployeeのID取得 const employee_id = $('#employee_id').val(); $.ajax('/avatar/' + employee_id + '/update', { method: "PATCH", // POSTの方が良いのかな? data: formData, processData: false, // 余計な事はせず、そのままSUBMITする設定? contentType: false, success: function (res) { // DOM操作にしたほうがいいのかな?その場合、アップロード後に実行するなどのポーリング処理的なサムシングが必要になりそう・・・ // なので、とりあえず簡単に`location.reload`しちゃう location.reload(); }, error: function (res) { console.error('Upload error'); } }); // S3にアップロードするため画質を50%落とす }, 'image/jpeg', 0.5); }); });
正直...
まだちょっとバギーだし、jQuery使いまくってるし、もうちょっとなんとかしたい感満載。