まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:doodle

「LINE 手描きスタンプ」「お絵描き共有サービス」「お絵描き Slack」などのお絵描き系(そんな系あるのか?)ウェブアプリを複数開発・運用しています。

これらのウェブアプリで使っている「お絵描きパネル」 UI 部分の作りはほぼ共通です。細かいことを言い出すと i18n 対応の有無とか、履歴呼び出しの有無とか、各アプリ毎に固有の情報を保存したりしなかったりしているので UI 部分含めて全くの共通ではないのですが、でも共通パーツが多いのも事実です。

今後も新しいアプリでこの「お絵描きパネル」を使うことも何度かあるだろう、というわけで、今後のアプリ開発を楽にする目的でパネル部分のモジュール化を地味に続けていました。とりあえず公開してもいいかな、というレベルになったので公開します。

ソースコードはこちらです:
https://github.com/dotnsf/doodlejs

実際に動くサンプルはこちら(スマホか PC のウェブブラウザでアクセスしてください):
https://dotnsf.github.io/doodlejs/

2021052501


実際の UI 含めた挙動は動くサンプルのページで確認してください。2021/05/24 時点では以下のような機能を持っています:
・PC のマウスやトラックボール/Windows タッチパネル/スマホのタッチでの描画に対応
・i18n は無し(画面は日本語のみ)
・線の色、太さを指定して描画する。指定した色を背景色に指定することもできる。
・アンドゥ・リドゥ・リセット可
・「送信」ボタンを押すと、描画内容を画像化し、エンコードした結果を console.log() で書き出す(スマホだと確認できません、ごめんなさい)
・「送信」ボタンを押した時の挙動はカスタマイズ化。実際に描画データをサーバー側へ送ってバックエンドに保存する、といったこともできる(ここまで含めてサンプルコードあり)


↑特に最後のカスタマイズ機能の目処がたったので公開を決断しました。サンプルのページだけでもお絵描きの描画は体験できます。が、実際にこのモジュールを組み込んで作るアプリでは描いた絵を保存する機能も必須だと思っています。一方、Github ページでサンプル公開しているとその部分の実現が難しく、保存するようなカスタマイズができる必要性も考慮した上で試行錯誤した結果、まあまあいい感じのバランスが取れた状態で公開しています。


【使い方】
自分のウェブアプリに組み込んで使う場合は以下の作業を行ってください:

①ソースコードを git clone またはダウンロード

フロントエンドで最低限必要なファイルは doodle.js ファイルと、サンプルページである index.html の2つです。

バックエンド側のカスタマイズも行う場合は、そのサンプルとなっている app.js ファイルも必要です。

②サンプルを元にウェブページを作成

フロントエンドのサンプル UI となる index.html を参考に(あるいはこのまま使って)ウェブページを用意します。同ウェブページには以下の要素が含まれている必要があります(サンプルの index.html にはすべて含まれています):

・お絵描き用のキャンバスを含むことになる <div id="cdiv"></div>
・色選択パーツの <select id="select_color"> ~ </select>
・線の太さ選択パーツの <select id="select_linewidth"> ~ </select>
・背景色変更ボタン <button onClick="setBG();"> ~ </button>
・アンドゥボタン <button onClick="undo();"> ~ </button>
・リドゥボタン <button onClick="redo();"> ~ </button>
・リセットボタン <button onClick="resetCanvas();"> ~ </button>
・送信ボタン <button onClick="sendCanvas();"> ~ </button>

ウェブページが用意できたら、画面ロード後にそれぞれの id 値を指定して以下の JavaScript を実行します(サンプルの index.html には既に含まれています):
$( function(){
  $('#cdiv').doodlejs({
    select_color: 'select_color',
    select_linewidth: 'select_linewidth',
    undo_btn: 'undo_btn',
    redo_btn: 'redo_btn',
    setbg_btn: 'setbg_btn'
  });
});

これで用意されたパーツ要素を使って、指定された <div id="cdiv"></div> 内に連動して動く Canvas が生成されます。

③「送信」ボタンのカスタマイズ

お絵描き後に「送信」ボタンを押した時の挙動をカスタマイズできます。上記サンプルでは単にお絵描き内容を画像化→エンコードして、結果のテキストを console.log() で出力しているだけですが、バックエンドに送信するようなケースを想定してカスタマイズできるようにしています。

送信ボタンをクリックした時の挙動をカスタマイズするには DOODLEJS.prototype.postCanvas() 関数を上書きします。この関数はお絵描き内容を画像化(png 化)したデータを引数に実行されます。プロトタイプでは以下の内容になっているので、その画像をエンコードして console.log() に出力しています:
(カスタマイズ前の処理)
DOODLEJS.prototype.postCanvas = function( png ){
  console.log( 'png', png );
};

例えば画像データをサーバーに送る場合は以下のような内容にプロトタイプを上書きします:
DOODLEJS.prototype.postCanvas = function( png ){
  //. バイナリ変換
  var bin = atob( png );
  var buffer = new Uint8Array( bin.length );
  for( var i = 0; i < bin.length; i ++ ){
    buffer[i] = bin.charCodeAt( i );
  }
  var blob = new Blob( [buffer.buffer], {
    type: 'image/png'
  });

  //. フォームにして送信
  console.log( 'Sending data... : ' + blob.size );
  var formData = new FormData();
  formData.append( 'image', blob ); 
  formData.append( 'timestamp', ( new Date() ).getTime() );

  $.ajax({
    type: 'POST',
    url: '/image',
    data: formData,
    contentType: false,
    processData: false,
    success: function( data, dataType ){
      console.log( data );
    },
    error: function( jqXHR, textStatus, errorThrown ){
      console.log( textStatus + ': ' + errorThrown );
    }
  });
};

これで画像データが POST /image という REST API でポストされることになります。後はバックエンド側でこのルーティングを処理するような REST API をバックエンドに用意し、送られてくる画像データを解析するなり、保存するなり、、といった処理を実装することになります(以下は app.js に含まれるサンプルで、特に保存せずに送信された画像の情報を返すだけの内容です):
app.post( '/image', function( req, res ){
  res.contentType( 'application/json; charset=utf-8' );

  if( req.file ){
    var imgpath = req.file.path;
    var imgtype = req.file.mimetype;
    var imgsize = req.file.size;
    //var imgfilename = req.file.filename;
    //var filename = req.file.originalname;

    var timestamp = parseInt( req.body.timestamp );

    var img = fs.readFileSync( imgpath );
    var img64 = new Buffer( img ).toString( 'base64' );
    fs.unlink( imgpath, function( err ){} );

    var params = {
      path: imgpath,
      type: imgtype,
      size: imgsize,
      timestamp: timestamp
    };
    console.log( params );
    var p = JSON.stringify( params, null, 2 );
    res.write( p );
    res.end();
  }else{
    res.status( 400 );
    res.write( JSON.stringify( { status: false, error: 'not initialized.' } ) );
    res.end();
  }
});

ウェブ画面内にマウスや指でお絵描きできるパーツ部品を組み込んで、送信後のデータ処理までカスタマイズするのに便利だと思っています。よかったら使ってください。


 

このブログエントリの続きです:
Slack の OAuth API を使ってみる

↑ここで紹介した方法を使って実際にアプリケーション・サービスを作ってみました。作った内容はこれ↓の Slack 版です:
お絵かき LIFF アプリを作ってみた

2019052501


(実はもともとは Twitter 向けに作ったのですが)LINE のフロントエンドフレームワーク: LIFF を使って、その場で指でお絵描きした画像を LINE のトークルームに送信する、という連携アプリケーションを以前に作りました。その Slack への移植です。

なお、このアプリケーションは Barloon というエンジニア向けバーで企画されたハッカソン向けに作成しました。興味ある方は "Barloon" でググって調べてみてください。


【作った物】
こちらで MIT ライセンスで公開しています。よかったら使ってください:
https://github.com/dotnsf/slack-doodle

2019052502



以下でも紹介していますが、注意点として「ワークスペースごとにアプリケーション・サーバーが1台必要」です。その理由ですが、このアプリは Slack API を利用して作っているのですが、Slack API に登録アプリを申請する際にワークスペース(https://○○○○.slack.com/ の ○○○○部分)を指定して申請する必要があるからです。ここで申請したワークスペース向けにアプリケーションを作って動かす形になるため、複数のワークスペースで動くアプリケーションを1つの URL で動かすことができないのでした(このあたりが上記の LINE 向けアプリと異なります)。


【サーバーの動かし方】
このアプリケーションは Node.js 上で動くウェブアプリケーションです。一応レスポンシブ対応しているつもりなので、PC ブラウザからも、スマホのブラウザからも使えます(PC ブラウザの場合はマウスで、スマホブラウザの場合は指でお絵描きすることになります)。したがって Node.js が導入済みのアプリケーション・サーバーが必要です。

利用にあたっては、まず Slack API のアプリケーション登録が必要です。こちらの詳しい手順はこのリンク先を参照してください:
http://dotnsf.blog.jp/archives/1074688701.html

ただし1点だけ注意が必要です。上記ページではアプリケーションの scope に channels:read のみを指定していますが、このお絵描きアプリケーションでは更に加えて:
 chat:write:user
 files:write:user
の2つ(計3つ)の scope を指定する必要があります(描いた画像を API でアップロードするために必要な scope です)。この3つを scope に指定する必要がある、という点に注意してください。

2019052503

※必要に応じてアプリケーションのアイコンなども好きなものに変えておいてください。


その上で上記 github の URLからソースコード一式を Node.js アプリケーションサーバー上に git clone するか download & unzip して、ソースコードを展開します。

展開後、settings.js というファイルが存在しているので、このファイルをテキストエディタで開き、exports.slack_client_id の値と、exports.slack_client_secret の値をそれぞれ Slack API 登録アプリの client_id および client_secret の値に書き換えて保存します(このあたりの具体的な情報はこちらを参照してください)。

そしてアプリケーションサーバーを起動、これで準備完了です:
$ npm install
$ node app

【遊び方】
アプリケーション・サーバー(例えば https://slack-doodle.xxx.com/ )が動いている状態で、そのアプリケーションサーバーの URL に PC かスマホのブラウザにアクセスするだけなのですが、その前にやっておくことがあります。

上記でも触れたのですが、このアプリケーションサーバーは特定のワークスペース向けに作られています(そのワークスペースでしか使えません)。一方、ブラウザで Slack にアクセスすると、いろんなワークスペースに切り替えて使うことができます。ということはブラウザが目的のワークスペース以外のセッションなどを保持している可能性があり、その状態で使っても期待通りの挙動にならない可能性があるのです。

この状態をクリアするために『念の為』以下の手順を最初に行っておくことを推奨します。まず PC かスマホのブラウザを起動し、目的のワークスペース(例えば目的のワークスペースが "abc" であれば https://abc.slack.com/ )にアクセスして、認証してログインします。これでブラウザが目的のワークスペースのセッションを保持した状態が作れました。

その上で、そのままアプリケーションサーバー( https://slack-doodle.xxx.com/ )にアクセスします。以下はスマホでの画面例ですが、PCブラウザだともう少し横に大きな画面になると思います(一応、この未ログインの時点でお絵描きを試すことはできるのですが、送信することはできません)。ではログインするため左上の "Login" をタップします:
2019052504


Slack アプリケーションのページに転送され、目的のワークスペースに向けた OAuth の認証が行われます:
2019052505


内容を確認して、「許可する」を選択します:
2019052506


するとログインが完了し、元のアプリの画面に戻ります。この時、画面上部にワークスペース上で自分が利用することのできるチャネルが選択できるようになり、POST をクリックすると、ここで選択したチャネルに描いた画像がアップロードされます:
2019052507


実際に POST するとこんな感じで目的のワークスペースの目的のチャネルに画像をアップロードすることができます:
2019052508


この系統のアプリ、Twitter ではじめて、LINE に移植して、今回は Slack にも移植して・・・ 次は何にしよう?? ちなみに facebook は publish の API が昨年廃止になってしまったので技術的に作れないことがわかってます。



このページのトップヘ