【GAS】手書き署名を収集できるWebフォームを作ってみた

Google Apps Script

とある案件で、「手書き署名を収集したい」というものがありました。
これに対するソリューションとして、GASのWebアプリケーションにてCanvasで手書きさせることを提案し、ひとまずは納品できました。
もちろん、署名自体はGoogleドライブに保存されるようになっています。
(Googleフォームで署名が簡単に収集できたら一番よかったのですが、現状は対応されていません。そのうちできるようになるかな、、?)

宣伝

例によって、GASスタンドで販売中ですので、よかったら買ってやってください。

手書き署名収集システム | GASスタンド
手書き署名収集システム 特徴 ・収集した署名画像はGoogleドライブに保存されます ・署名保存先となるGoogleドライブのフォルダIDを指定し、デプロイするだけの簡単設定です ・Webアプリケーションなので、不特定多数からのアクセスに対応することができます 使い方 本システムの使い方は下記となります。 1.署名画像...

完成形

こんな感じの見た目になります。

このフォームを送信すると、Googleドライブに署名画像が保存されるのと同時に、スプレッドシートにデータが蓄積されます。

技術的な話

ここからは技術的に頑張った話を書いていきます。

React + Webアプリケーション

最近はReactを使ってWebアプリケーションを作ることが多いです。
ノウハウは下記にまとめてありますので、是非ご覧ください。

GASでReact + Redux(+ Material UI)なWebアプリケーションを作ってみた
むしゃくしゃしてやった。後悔はしている。(時間かけすぎた)ということで、タイトルの通りなのですが、GASのWebアプリケーションにReact + Reduxを組み込んだものを作ってみました。その時の知見を書き綴っていこうと思います...

Canvasによる手書き

ここは結構頑張りました。
以下に、GASで使える <Signature> コンポーネントを置いておきます。

<script type="text/babel">
  const Signature = (props) => {
    const { makeStyles, Button } = MaterialUI;

    const classes = makeStyles({
      root: {
        padding: '15px 0',
        width: '100%'
      },
      canvas: {
        border: '1px solid #000000',
        borderRadius: '7px',
        touchAction: 'none',
      }
    })();

    // canvasはDOMを直接操作するためuseRef()経由で操作する
    const canvas = React.useRef(null);
    // ドラッグ中判断フラグ(マウスを離すか、canvas外へ出たらfalse)
    const [drawing, setDrawing] = React.useState(false);

    const clear = () => {
      const ctx = canvas.current.getContext('2d');
      if( ctx ) {
        ctx.clearRect(0, 0, props.width, 200);
        if (props.onUpdateCanvas) props.onUpdateCanvas(null);
      }
    };

    // 描画に必要なcontextを取得し、線の色、幅をセットする
    const getContext = () => {
      const ctx = canvas.current.getContext('2d');
      ctx.lineWidth = props.lineWidth;
      ctx.lineCap = props.lineCap;
      ctx.strokeStyle = props.lineColor;
      return ctx;
    }

    // 線描画開始処理。beginPath()で新しいパスを開始する(開始しないと色や太さが変更できない)
    const drawStart = (e) =>  {
      const { offsetX: x, offsetY: y } = offsetPosition(e);
      setDrawing(true);
      const ctx = getContext();
      ctx.beginPath();
      ctx.moveTo(x, y);
    }

    // 動きに合わせて線を描画する
    const drawMove = (e) => {
      if (!drawing) return;
      const { offsetX: x ,offsetY: y } = offsetPosition(e);
      const ctx = getContext();
      ctx.lineTo(x, y);
      ctx.stroke();
    } 

    // 線描画完了(canvas更新イベントコールバックを行う)
    const endDrawing = () => {
      setDrawing(false);
      if (props.onUpdateCanvas) props.onUpdateCanvas(canvas.current);
    }

    // offset(canvas左上からの)を返す。Touch,Mouseイベント両対応
    const offsetPosition = (e) => {
      if (e.nativeEvent instanceof TouchEvent) {
        const rect = e.target.getBoundingClientRect();
        const offsetX = (e.nativeEvent.touches[0].clientX - rect.left);
        const offsetY = (e.nativeEvent.touches[0].clientY - rect.top);
        return { offsetX, offsetY };
      } else if (e.nativeEvent instanceof MouseEvent) {
        return { offsetX: e.nativeEvent.offsetX ,offsetY: e.nativeEvent.offsetY };
      }
    }

    return (
      <div className={classes.root}>
        <canvas 
          ref={canvas}
          width={props.width}
          height={200}
          className={classes.canvas}
          onMouseDown={drawStart} 
          onMouseMove={drawMove} 
          onMouseUp={endDrawing}
          onMouseLeave={endDrawing}
          onTouchStart={drawStart} 
          onTouchMove={drawMove} 
          onTouchEnd={endDrawing} />
        <Button onClick={clear} variant="contained">署名をクリア</Button>
      </div>
    );
  }
  Signature.defaultProps = {
    width: 500,
    lineWidth: 3,
    lineColor: "black",
    lineCap: "round",
  };
</script>

このコンポーネントを使うときは、こんな感じです。
props.widthonUpdateCanvas は定義済みとします)

<Signature width={props.width} onUpdateCanvas={onUpdateCanvas} />

終わりに

GAS+ReactでCanvasを扱うのは少し大変でした。
しつこいですが、よければGASスタンドを覗いていってください(宣伝)

手書き署名収集システム | GASスタンド
手書き署名収集システム 特徴 ・収集した署名画像はGoogleドライブに保存されます ・署名保存先となるGoogleドライブのフォルダIDを指定し、デプロイするだけの簡単設定です ・Webアプリケーションなので、不特定多数からのアクセスに対応することができます 使い方 本システムの使い方は下記となります。 1.署名画像...
タイトルとURLをコピーしました