the industrial

都内で働くITエンジニアの日記

サカサマのパテマ

サカサマのパテマ

アマゾンプライムビデオで評価が高かったので鑑賞。

世界観や、設定などは「アップサイドダウン - 重力の恋びと - the industrial」そのもの。

かなり上手くまとまっていて、こちらもとてもおもしろかった。

メイド・イン・アビス

メイドインアビス Blu-ray BOX 下巻

Twitterで話題だったので見たのだけど、世界観含めてめちゃくちゃおもしろかった。

原作はネットで連載中なので、1期というくくり。

続きが本当に気になる。

シュタインズ・ゲート

STEINS;GATE -シュタインズ・ゲート-:コンプリート・シリーズ 廉価版 北米版 / Steinsgate: Complete Series Classic [Blu-ray+DVD][Import]

同僚にオススメして見たアニメ。

言うなれば映画バタフライ・エフェクトのアニメ版の様な感じなのだけど、ラストまで引き込まれるストーリーの脱帽。

Rails5 + Cropper.js + carrierwave で作る画像クロップ処理

作ったもの

よく、SNSで利用するようなアイコンをアップロードする際、好きな箇所で切り取る処理を作成してみたのでメモがてら書いていく。

こんな感じの動き。

f:id:omiend:20171003152633g:plain

開発環境について

前回作成した下記エントリーの開発環境をそのまま利用。

omiend.hatenablog.jp

変更点として、users Model と employees Modelの使い方を、思っていたのと間違えていたっぽいので修正。

  • users : deviseで利用するアカウント情報として利用
  • employees : 社員管理

ソース

コチラ に配置しているので、よかったらツッコミおなしゃす。

docker−compose環境があれば、my_strongest_ror/docker/start.sh を叩くだけで動きます(たぶん)。

準備 - Cropperのダウンロード&配置

Cropper.js から cropper.min.jscropper.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使いまくってるし、もうちょっとなんとかしたい感満載。

ローグ・ワン

ローグ・ワン/スター・ウォーズ・ストーリー (吹替版)

今さらなんだけど、読めちゃんと鑑賞。

デス・スターの設計図を盗み出すというミッションをやってのけた「ローグ・ワン」たちの物語。

STARWARSではエピソード3と4の間に位置する話で、すっごく楽しかった!

潜水服は蝶の夢を見る

潜水服は蝶の夢を見る (字幕版)

脳梗塞で「ロックトイン・シンドローム」となり、左目しか動かせなくなった実在の人物、ELLEの編集長、ジャン・ドミニック・ボービーを題材にした映画。

その後、左目の”まばたきだけで”自伝を書き上げたのは本当にすごい。

主演はマチュー・アマルリック」という俳優さんで、グランド・ブダペスト・ホテルなどに出演。

結構泣ける。

ダークシティ

ダークシティ (字幕版)

なんとなく見出した映画。

ジャック・バウアー役の人が出ている以外は、つまらなくはないけど、なんだかなあというSF映画だった。