ラベル drawingboard の投稿を表示しています。 すべての投稿を表示
ラベル drawingboard の投稿を表示しています。 すべての投稿を表示

2021年12月23日木曜日

drawingboard.js でサーバサイドに画像を保存する方法

drawingboard.js でサーバサイドに画像を保存する方法

概要

前回 drawingboard.js に入門してみました
今回はサーバサイドに作成した画像を保存してみます
サーバサイドは Ruby で実装します

環境

  • macOS 11.6.2
  • Ruby 3.0.3p157
  • sinatra 2.1.0
  • drawingboard.js 0.4.2

config.ru

require './app'

run TestWeb

app.rb

require 'sinatra'
require 'base64'

class TestWeb < Sinatra::Base
  get '/' do
    erb :index
  end

  post '/' do
    filename = params[:filename]
    image = params[:image]
    image = Base64.decode64(image.gsub('data:image/png;base64,', '').gsub(/\s/, '+'))
    File.open("/tmp/#{filename}.png", 'wb') do |file|
      file.write(image)
      'ok'
    end
  end
end

index.erb

<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width">
  <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/drawingboard.js/0.4.2/drawingboard.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/drawingboard.js/0.4.2/drawingboard.min.css">
  <style data-example="2">
  #board1 {
    width: 400px;
    height: 400px;
  }
  </style>
</head>
<body>
  <div class="board" id="board1"></div>
  <input id="filename" type="text" value="image1" />
  <button id="save">Save</button>
  <script data-example="2">
  var board1 = new DrawingBoard.Board('board1', {
    background: "#000000",
    color: "#ffffff",
    size: 30,
    fillTolerance: 150,
    controls: [
      { Size: { type: "range", min: 12, max: 42 } },
      { Navigation: { back: true, forward: true } },
      'DrawingMode',
      'Color'
    ],
    webStorage: 'local',
    droppable: true
  });
  board1.addControl('Download');
  board1.downloadImg = function() {
    var img = this.getImg();
    img = img.replace("image/png", "image/octet-stream");
    var link = document.createElement('a');
    link.download = "download.png";
    link.href = img;
    link.click();
  };
  $('#save').on('click', function(e) {
    var img = board1.getImg();
    var filename = $("#filename").val();
    $.ajax({
      type: "POST",
      url: "/",
      enctype: 'multipart/form-data',
      data: "image=" + img + '&filename=' + filename,
      success: function(msg){
        alert( "Data Saved: " + msg );
      },
      error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert("some error");
      }
    });
  });
  </script>
</body>
</html>

ちょっと解説

画像情報を送信する場合は base64 エンコードされたデータを文字列で送信します
わざわざバイナリ変換してファイルとして送信する方法もありますが文字列のほうが扱いが楽なので文字列のまま使います

base64 の文字列を送信する場合は ajax を使います
POST で multipart/form-data として送信します
こうすることで一緒にファイル名も送信できます

Ruby (sinatra) 側ではフォームデータとして画像データとファイル名を取得します
それぞれ params から文字列として取得できます
画像データは不要な文字列をエスケープしスペースをプラスに置換します

あとは Base64.decode しファイルをバイナリ形式で保存すれば OK です
実際にファイル化するのが嫌な場合は Redis などに文字列としてそのまま保存すると良いかなと思います

最後に

なぜか ajax から受け取った画像の base64 データがサーバサイドだと一部置換されているのでそこをちゃんと補完してあげることを忘れないようにしましょう

参考サイト

2021年12月21日火曜日

drawingboard.js 超入門

drawingboard.js 超入門

概要

drawingboard.js を使うと HTML だけで簡単にペイント機能が追加できます

環境

  • macOS 11.6.2
  • Chrome 96.0.4664.110
  • drawingboard.js 0.4.2

最小サンプルコード

index.html だけで動作します
サーバなどへのデプロイも不要です

  • vim index.html
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width">
  <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/drawingboard.js/0.4.2/drawingboard.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/drawingboard.js/0.4.2/drawingboard.min.css">
  <style data-example="2">
  #board1 {
    width: 400px;
    height: 400px;
  }
  </style>
</head>
<body>
  <div class="board" id="board1"></div>
  <script data-example="2">
  var board1 = new DrawingBoard.Board('board1', {
    background: "#000000",
    color: "#ffffff",
    size: 30,
    fillTolerance: 150,
    controls: [
      { Size: { type: "range", min: 12, max: 42 } },
      { Navigation: { back: true, forward: true } },
      'DrawingMode',
      'Color'
    ],
    webStorage: 'local',
    droppable: true
  });
  board1.addControl('Download');
  board1.downloadImg = function() {
    var img = this.getImg();
    img = img.replace("image/png", "image/octet-stream");
    var link = document.createElement('a');
    link.download = "download.png";
    link.href = img;
    link.click();
  };
  </script>
</body>
</html>

ちょっと解説

まず div タグを作成し id を振ります
その id を元に new DrawingBoard.Board でボードを作成します

引数の2つ目でいろいろとオプションを指定できます
特に controls が重要でこれで消しゴムやペン、塗りつぶしなど様々なコントローラを設定できます
コントローラに指定可能な値は全部で4つで「Color」「DrawingMode」「Size」「Navigation」がありそれぞれ更にオプションを指定できます

ダウンロードボタンだけ board1.addControl('Download') で設定します デフォルトだと「ダウンロード」というファイル名でダウンロードされてしまうので拡張しなど指定したい場合は downloadImg 関数をオーバライドすることで指定できます

動作確認

  • open index.html

こんな感じでペイント画面がブラウザで表示されれば OK です

最後に

簡単なペイント機能であれば簡単に追加できそうです

各種コントローラなどのボタンのイベントをハンドリングすれば作成した画像をサーバサイドに保存することもできそうです

ただあまりメンテナンスされていないのでそのうち動かなくなる可能性はありそうです

参考サイト