新規投稿
フォローする

p5.jsを使って画像にメモ書き

p5.jsは、容易にグラフィックやサウンドを操作できるJavaScriptライブラリです。 詳しいプログラミングの知識がなくても、アプリケーションやアニメーション、ゲームなどをブラウザ上で表現できるようになります。 今回は、ブラウザ上で画像にメモ書きしながらアップロードする機能を作成しました。

サンプル

フォーム設定

コード

本サンプルでは、p5.min.js及びp5.dom.min.jsを利用しています。 p5.min.jsのみでも同様の機能の実装は可能ですが、p5.dom.min.jsを利用することでより簡単にinput要素を作成しています。
まず、p5.min.jsとp5.dom.min.jsを登録したのち、下記sample.jsを登録します。
※p5.js v0.7.1で動作を確認しています。

sample.js

(function() {
  "use strict";
  var myp5;
  kintone.events.on([
    'app.record.create.show',
    'app.record.edit.show',
  ], function(event){
    kintone.app.record.setFieldShown('添付ファイル', false);
    kintone.app.record.getSpaceElement('space').innerHTML = '<div class="control-label-gaia"><span class="control-label-text-gaia">添付ファイル</span></div><div id="p5Container"></div>';
    myp5 = new p5((function(p){
      var color, weight;
      var showImage = function(image){
        p.resizeCanvas(image.width, image.height);
        p.canvas.style.width = '';
        p.canvas.style.height = '';
        p.background(image);
      };
      p.setup = function(){
        if(event.type === 'app.record.create.show'){
          p.file = {
            name: 'image.png',
            type: 'image/png',
          };
          p.createCanvas(500, 300);
          p.background(255);
        }else{
          p.file = {
            name: event.record.添付ファイル.value[0].name,
            type: event.record.添付ファイル.value[0].contentType,
          };
          var xhr = new XMLHttpRequest();
          xhr.open('GET', '/k/v1/file.json?fileKey=' + event.record.添付ファイル.value[0].fileKey);
          xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
          xhr.responseType = 'blob';
          xhr.addEventListener('load', function(){
            p.loadImage((window.URL || window.webkitURL).createObjectURL(xhr.response), showImage);
          });
          xhr.send();
        }
        p.createFileInput(function(file){
          if(file.type !== 'image') return;
          p.file = {
            name: file.name,
            type: 'image/' + file.subtype,
          };
          p.loadImage(file.data, showImage);
        });
        color = p.createInput('#000');
        weight = p.createInput(10, 'number');
      }
      p.draw = function(){
        p.stroke(color.elt.value);
        p.strokeWeight(weight.elt.value);
        if (p.mouseIsPressed === true) {
          p.line(p.mouseX, p.mouseY, p.pmouseX, p.pmouseY);
        }
      }
    }), 'p5Container');
  });
  kintone.events.on([
    'app.record.create.submit.success',
    'app.record.edit.submit.success',
  ], function(event){
    return new kintone.Promise(function(resolve){
      myp5.canvas.toBlob(function(blob){
        var formData = new FormData();
        var xhr = new XMLHttpRequest();
        formData.append('__REQUEST_TOKEN__', kintone.getRequestToken());
        formData.append('file', blob, myp5.file.name);
        xhr.open('POST', encodeURI('/k/v1/file.json'));
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.addEventListener('load', function(){
          kintone.api('/k/v1/record', 'PUT', {
            app: kintone.app.getId(),
            id: event.recordId,
            record: {
              添付ファイル: {
                value: [
                  {fileKey: JSON.parse(xhr.responseText).fileKey}
                ]
              }
            }
          }).then(function(){
            resolve(event);
          });
        });
        xhr.send(formData);
      }, myp5.file.type);
    });
  });
})();
if(!HTMLCanvasElement.prototype.toBlob){ //polyfill
  Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
    value: function(callback, type, quality){
      var binStr = atob(this.toDataURL(type, quality).split(',')[1]),
        len = binStr.length,
        arr = new Uint8Array(len);
      for(var i=0; i<len; i++ ){
        arr[i] = binStr.charCodeAt(i);
      }
      callback(new Blob([arr], {type: type || 'image/png'}));
    }
  });
}
3

4件のコメント

Avatar
N_okazaki

いつも参考にさせていただいております。

このカスタマイズでデザインの校正アプリが作れそうで、大変助かっています。

 

1つ質問ですが、消しゴム機能を追加することは可能でしょうか。

現状では書き間違えると保存せずに閉じるしかないので、kintone上で書き込みした部分だけ消せるととても便利なのですが。

自分なりに調べてみましたが、どうにも方法が見つかりませんでした。

0
Avatar
江田篤史

N_okazaki様

お世話になっております。
コメントありがとうございます。

下記のように変更して、ボタンを追加するとよいと思います。

...
    myp5 = new p5((function(p){
      var color, weight, clear, displayedFile;
      var showImage = function(image){
        p.resizeCanvas(image.width, image.height);
        p.canvas.style.width = '';
        p.canvas.style.height = '';
        p.background(image);
      };
      p.setup = function(){
        if(event.type === 'app.record.create.show'){
          p.file = {
            name: 'image.png',
            type: 'image/png',
          };
          p.createCanvas(500, 300);
          p.background(255);
        }else{
          p.file = {
            name: event.record.添付ファイル.value[0].name,
            type: event.record.添付ファイル.value[0].contentType,
          };
          var xhr = new XMLHttpRequest();
          xhr.open('GET', '/k/v1/file.json?fileKey=' + event.record.添付ファイル.value[0].fileKey);
          xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
          xhr.responseType = 'blob';
          xhr.addEventListener('load', function(){
            displayedFile = (window.URL || window.webkitURL).createObjectURL(xhr.response);
            p.loadImage(displayedFile, showImage);
          });
          xhr.send();
        }
        p.createFileInput(function(file){
          if(file.type !== 'image') return;
          displayedFile = file.data;
          p.file = {
            name: file.name,
            type: 'image/' + file.subtype,
          };
          p.loadImage(displayedFile, showImage);
        });
        color = p.createInput('#000');
        weight = p.createInput(10, 'number');
        clear = p.createButton('clear');
        clear.mousePressed(function(){
          if(displayedFile){
            p.loadImage(displayedFile, showImage);
          }else{
            p.background(255);
          }
        });
      }
      p.draw = function(){
        p.stroke(color.elt.value);
        p.strokeWeight(weight.elt.value);
        if (p.mouseIsPressed === true) {
          p.line(p.mouseX, p.mouseY, p.pmouseX, p.pmouseY);
        }
      }
    }), 'p5Container');
...
0
Avatar
N_okazaki

江田様

 

早速ご返信いただきありがとうございます。

コードまでいただき、とても助かりました。

まさしく、これがやりたかった!という動作です。早速使っていきたいと思います。

0
Avatar
harada

サンプルを動かしたのですが、手書きをして保存、手書きをして保存を繰り返すと画像が大きくなりませんか?

どこを直したらいいのかわからなかったので気づいたことだけお伝えします。

0
サインインしてコメントを残してください。