html2canvasでスクショを撮ってyabumiにアップロードする


先週あたりからなんとなくTypeScriptに興味を持ち始めて、どんなものなのか触ってみてます。
何となくお題になりそうなのを探してたら、teratailで下記の記事を見つけたので、TypeScriptで書いてみようかと思い試行錯誤してました。
- [Javascriptを使い非同期でAPIにCanvas上に表示された画像をPOSTして返り値を処理する方法](https://teratail.com/questions/16506)

ソースコードはGithubにおいてます。
https://github.com/328/typescript-practise/tree/master/html2canvas


```dirtree
 ~/screenshot-js
 $ tree -L 2
.
├ ─ ─  build
│    └ ─ ─  lib
│        ├ ─ ─  yabumi.js
│        └ ─ ─  yabumi.js.map
├── bundle.js
├── index.html
├── lib
│   └── yabumi.ts
├── node_modules
├── package.json
├── tsconfig.json
├── tslint.json
└── yarn.lock
```

## ライブラリたち
```package.json
{
  "name": "screenshot-js",
  "version": "1.0.0",
  "main": "index.js",
  "author": "328",
  "license": "MIT",
  "dependencies": {
    "browserify": "",
    "tslint": "",
    "jquery": "",
    "html2canvas": "",
    "@types/html2canvas": ""
  }
}
```

- html2canvas
webページで表示されている内容をcanvasエレメント上に描画するやつ
- jquery
ajaxでurlにポストしたいので入れました
- tslint
typescriptの構文チェックに利用。tslint.jsonには、tslint:recommendedだけ記述
- browserify
Nodeモジュールをブラウザでも利用するために導入
- @type
typescriptの型定義


## tsconfig
書き方合ってるか分かんない...

```tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es5",
    "noImplicitAny": false,
    "sourceMap": true,
    "outDir": "build",
    "rootDir": "."
  }
}

```

## ソースコード

```lib/yabumi.ts
import * as html2canvas from "html2canvas";
import * as $ from "jquery";

function image_generator() {
  let dfd = $.Deferred();
  html2canvas(document.body, {
    onrendered (canvas: any) {
      let formData = Canvas2FormData(canvas);
      dfd.resolve(formData);
    },
  });
  return dfd.promise();
}

function Canvas2FormData(canvas: any) {
  let base64Data: string = canvas.toDataURL().split(",")[1];
  let data: any = window.atob(base64Data);
  let buff: any = new ArrayBuffer(data.length);
  let arr: any = new Uint8Array(buff);

  for (let i: number = 0; i < data.length; i++) {
    arr[i] = data.charCodeAt(i);
  }

  let blob: any = new Blob([arr], {type: "image/png"});
  let formData: any = new FormData();
  formData.append("imagedata", blob);
  return formData;
}

function image_post(images: any) {
  let dfd = $.Deferred();
  $.ajax({
    cache: false,
    contentType: false,
    data: images,
    processData: false,
    type: "POST",
    url: "https://yabumi.cc/api/images.json",
  }).done(function(data, textStatus, xhr) {
    console.info(data);
    dfd.resolve(data);
  }).fail(function(xhr, textStatus, error) {
    console.error("error");
    console.error(xhr);
    console.error(textStatus);
    console.error(error);
  });
  return dfd.promise();
}

$(function () {
  $("#screenshots").on("click", function() {
    image_generator()
      .then(function(image) { return image_post(image); });
  });
});
```
image_generatorでは、body要素をhtml2canvasに投げる。
Canvas2FormDataでは、canvasデータをBlobに変換し、formDataに突っ込んでそれを返す
image_postでは、ajax使ってyabumiにpostしてます。レスポンスの結果はconsoleに出す感じで。
screenshotsというidが振られたbuttonがクリックされたら画像がyabumiにポストされる。

tscコマンドでjavascriptにトランスパイルして、browserifyコマンドでbundle.jsを生成する。

ただ、上記のソースをtscコマンドでトランスパイルする際に、下記のエラーが発生した。

```error.log
error TS2345: Argument of type '{ onrendered: (canvas: any) => void; }' is not assignable to parameter of type 'Html2CanvasOptions'.
  Object literal may only specify known properties, and 'onrendered' does not exist in type 'Html2CanvasOptions'.
```
どうやら、型定義ファイルにonrenderedが無いのが原因らしい。
型定義ファイルを自分で作って読み込ませるか、あるいはPR出したほうがいいのだけれど、めんどくさくって直接追記した。

```node_modules/@types/html2canvas/index.d.ts
declare namespace Html2Canvas {
    interface Html2CanvasOptions {
        /** Whether to allow cross-origin images to taint the canvas */
        allowTaint?: boolean;

        /** Canvas background color, if none is specified in DOM. Set undefined for transparent */
        background?: string;

        /** Define the heigt of the canvas in pixels. If null, renders with full height of the window. */
        height?: number;

        /** Whether to render each letter seperately. Necessary if letter-spacing is used. */
        letterRendering?: boolean;

        /** Whether to log events in the console. */
        logging?: boolean;

        /** Url to the proxy which is to be used for loading cross-origin images. If left empty, cross-origin images won't be loaded. */
        proxy?: string;

        /** Whether to test each image if it taints the canvas before drawing them */
        taintTest?: boolean;

        /** Timeout for loading images, in milliseconds. Setting it to 0 will result in no timeout. */
        timeout?: number;

        /** Define the width of the canvas in pixels. If null, renders with full width of the window. */
        width?: number;

        /** Whether to attempt to load cross-origin images as CORS served, before reverting back to proxy. */
        useCORS?: boolean;

        /** Use svg powered rendering where available (FF11+). */
        svgRendering?: boolean;

         /** 追記 **/
        /** Callback providing the rendered canvas element after rendering */
        onrendered?(canvas: HTMLCanvasElement): void;
    }
}
```


```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>テストページ</title>
</head>
<body>
  <h2>やふー！</h2>
  <ul>
    <li>スクショ撮ってみる！</li>
    <li>あーーーーーーーーーーーーー</li>
    <li>あーーーーーーーーーーーーー</li>
    <li>あーーーーーーーーーーーーー</li>
    <li>あーーーーーーーーーーーーー</li>
    <li>あーーーーーーーーーーーーー</li>
    <li>あーーーーーーーーーーーーー</li>
  </ul>
  <button id="screenshots" type="button">Log Output</button>
  <script src="bundle.js"></script>
</body>
</html>
```
buttonにidを振ってるのと、javascriptを読み込ませてるだけ。


という感じで、無事にスクショをyabumiにアップするまでできました。
tslintで、構文チェックしてると下記の部分で`non-arrow functions are forbidden`と言われる。

```
$(function () {
  $("#screenshots").on("click", function() {
    image_generator()
      .then(function(image) { return image_post(image); });
  });
});
```
このあたりをアロー関数で書き直すべきなのだろうか。

```
$(() =>
  $("#screenshots").on("click", () =>
    image_generator()
      .then((image) => image_post(image)),
  ),
);
```
書き直してみたらすごい見づらくなった気がするんだけど気のせいだろうか。
ajaxの部分も`non-arrow functions are forbidden`と出てたので、この辺りもどうにかこうにか書き直す必要があるんだろうけど、体力がなくなったのでこの辺でおしまい。

まだ分かってない部分が多すぎて、何から手をつけていいのかわからず状態です...
毎回手でコマンドを発行するのが億劫になってきたので、タスクランナーとか入れてみたい。
gulpとかgruntとかあるけど、どちらがいいのだろうか...