スマホやタブレットが増えてきて、Railsアプリでも手書きで書いた絵や図を取り込みたいというような案件があるのではないでしょうか?手書き画像が保存する方法はいろいろありますが、そのひとつをちょっとメモのため残しておきます。特別なgemとかは必要ありません。使うのは HTML5 の canvas だけです。

環境

  • Ruby 2.0.0p353
  • Rails 4.0.1
  • bundler

手順

まず、Rails 環境を作成して足場をつくります。

Terminal

$
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
$
$
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
$
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
$
 
 
 
 
rails new blog --skip-bundle      create
      create  README.rdoc
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/assets/javascripts/application.js
      create  app/assets/stylesheets/application.css
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/views/layouts/application.html.erb
      create  app/assets/images/.keep
      create  app/mailers/.keep
      create  app/models/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/bundle
      create  bin/rails
      create  bin/rake
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/backtrace_silencers.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/mime_types.rb
      create  config/initializers/secret_token.rb
      create  config/initializers/session_store.rb
      create  config/initializers/wrap_parameters.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/favicon.ico
      create  public/robots.txt
      create  test/fixtures
      create  test/fixtures/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/test_helper.rb
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor/assets/javascripts
      create  vendor/assets/javascripts/.keep
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.keep
cd blogbundle install --path vendor/bundleFetching gem metadata from https://rubygems.org/……….
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies…
Installing rake (10.1.0)
Installing i18n (0.6.5)
Installing minitest (4.7.5)
Installing multi_json (1.8.2)
Installing atomic (1.1.14)
Installing thread_safe (0.1.3)
Installing tzinfo (0.3.38)
Installing activesupport (4.0.1)
Installing builder (3.1.4)
Installing erubis (2.7.0)
Installing rack (1.5.2)
Installing rack-test (0.6.2)
Installing actionpack (4.0.1)
Installing mime-types (1.25.1)
Installing polyglot (0.3.3)
Installing treetop (1.4.15)
Installing mail (2.5.4)
Installing actionmailer (4.0.1)
Installing activemodel (4.0.1)
Installing activerecord-deprecated_finders (1.0.3)
Installing arel (4.0.1)
Installing activerecord (4.0.1)
Using bundler (1.3.1)
Installing coffee-script-source (1.6.3)
Installing execjs (2.0.2)
Installing coffee-script (2.2.0)
Installing thor (0.18.1)
Installing railties (4.0.1)
Installing coffee-rails (4.0.1)
Installing hike (1.2.3)
Installing jbuilder (1.5.2)
Installing jquery-rails (3.0.4)
Installing json (1.8.1)
Installing tilt (1.4.1)
Installing sprockets (2.10.1)
Installing sprockets-rails (2.0.1)
Installing rails (4.0.1)
Installing rdoc (3.12.2)
Installing sass (3.2.12)
Installing sass-rails (4.0.1)
Installing sdoc (0.3.20)
Installing sqlite3 (1.3.8)
Installing turbolinks (1.3.1)
Installing uglifier (2.3.2)
bundle exec rails g scaffold post      invoke  active_record
      create    db/migrate/20131203101546_create_posts.rb
      create    app/models/post.rb
      invoke    test_unit
      create      test/models/post_test.rb
      create      test/fixtures/posts.yml
      invoke  resource_route
       route    resources :posts
      invoke  scaffold_controller
      create    app/controllers/posts_controller.rb
      invoke    erb
      create      app/views/posts
      create      app/views/posts/index.html.erb
      create      app/views/posts/edit.html.erb
      create      app/views/posts/show.html.erb
      create      app/views/posts/new.html.erb
      create      app/views/posts/_form.html.erb
      invoke    test_unit
      create      test/controllers/posts_controller_test.rb
      invoke    helper
      create      app/helpers/posts_helper.rb
      invoke      test_unit
      create        test/helpers/posts_helper_test.rb
      invoke    jbuilder
      create      app/views/posts/index.json.jbuilder
      create      app/views/posts/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/posts.js.coffee
      invoke    scss
      create      app/assets/stylesheets/posts.css.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.css.scss
bundle exec rake db:migrate==  CreatePosts: migrating ====================================================
– create_table(:posts)
   -> 0.0010s
==  CreatePosts: migrated (0.0011s) ===========================================

app/views/posts/_form.html.erb を開いて次のコードを追加します。このコードが手書き入力部分(canvas)になります。

app/views/posts/_form.html.erb
1
2
3
4
5
  <canvas id="paper" class="paper" width="400" height="400" data-target="#sign">
    ご利用のブラウザではサインは表示できません。
  </canvas>
  <button type="button" name="clearCanvas" class="clear-canvas">クリア</button>
  <input type="hidden" id="sign" name="sign" value="" />

app/assets/javascripts に paint.js.coffee ファイルを作成し、次のコードをコピペします。

app/assets/javascripts/paint.js.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
28
29
30
31
32
33
34
35
36
jQuery ($) ->
    screen2client = (e) ->
        [px, py] = if e.type.startsWith('touch') then [e.originalEvent.touches[0].clientX, e.originalEvent.touches[0].clientY] else [e.clientX, e.clientY]
        rect = e.target.getBoundingClientRect()
        {x: px - rect.left, y: py - rect.top}
    drawFlag = false
    oldX = 0
    oldY = 0
    $canvas = $('#paper')
    $canvas.on 'mousedown touchstart', (e) ->
        drawFlag = true
        {x, y} = screen2client(e)
        [oldX, oldY] = [x, y]
    $canvas.on 'mouseup mouseout touchend touchcancel', ->
        drawFlag = false
        data = $canvas[0].toDataURL()
        $($canvas.data('target')).val(data)
    $canvas.on 'mousemove touchmove', (e) ->
        return unless drawFlag
        {x, y} = screen2client(e)
        context = $canvas[0].getContext('2d')
        context.strokeStyle = '#C71C22'
        context.lineWidth = 1
        context.beginPath()
        context.moveTo(oldX, oldY)
        context.lineTo(x, y)
        context.stroke()
        context.closePath()
        oldX = x
        oldY = y
        e.preventDefault()
        return
    $('.clear-canvas').on 'click', ->
        context = $canvas[0].getContext('2d')
        context.clearRect(0, 0, $canvas[0].width, $canvas[0].height)
        $($canvas.data('target')).val(null)

app/assets/javascript/application.js を開き、ファイルの最後に次のコードを追加します。

app/assets/javascript/application.js
1
2
3
4
5
if (typeof String.prototype.startsWith != 'function') {
    String.prototype.startsWith = function(str) {
        return this.substring(0, str.length) === str;
    }
}

最後に、画像データを受け取って画像ファイルにする処理をコントローラに追加します。app/controllers/posts_controller.rb を開き、def create に次のコードを追加します。

app/controllers/posts_controller.rb
1
2
3
f = Tempfile.new(['sign', '.png'], encoding: Encoding::BINARY)
f.write Base64.decode64(params[:sign].sub('data:image/png;base64,', ''))
f

これで、一時ファイルにキャンバスデータを書き込んでファイル化できます。サーバを起動して http://localhost:3000/posts/new にアクセスして試してみてください。

スクリーンショット