Sibainu Relax Room

愛犬の柴犬とともに過ごす部屋

Javascript html2canvas の戻り値は Promise になっている

ここはまだ桜が咲いているぞ。すこし変わった花だなと考えているような顔している柴犬です。

明日からいなざわ植木まつりが始まります。天気がよさそうですので賑やかになりそうです。

概略

ブラウザをスクリーンショット、プリントスクリーンで画像化はできるのですが、ウインドウに入りきらない大きな表などの画像化は重複、抜けなどに注意しながら複数枚撮ることになります。

ちょっと面倒なことになります。

それで、利用場面は多くありませんが考えてみました。

調べてみると html2canvas が使えそうというかその目的そのままのライブラリーがありますのでそれを使うことにします。

これは、見ているブラウザそのものの画像ではなく、html2canvas が解釈して画像化したものですが、このケースでは結構再現することができました。

画像化するサンプルは次のものです。

HTML

div で表を作ってみました。

copy

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="./styles/style4.css" rel="stylesheet" />

</head>
<body>

<div id="htmlcanvas">
    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_id border_3">1</div>
          <div class="d_content border_3">
    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan1 border_1_4">data1-1</div>
          <div class="d_colspan1 border_1">data1-2</div>
          <div class="d_colspan1 border_1">data1-3</div>
          <div class="d_colspan1 border_1">data1-4</div>
          <div class="d_colspan1 border_1_2">data1-5</div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan2 border_1_3_4">data2-1</div>
          <div class="d_colspan1 border_1_3">data2-3</div>
          <div class="d_colspan1 border_1_3">data2-4</div>
          <div class="d_colspan1 border_1_2_3">data2-5</div>
        </div>
      </div>
    </div>
          </div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_id border_3">2</div>
          <div class="d_content border_3">

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan1 border_1_4">data1-1</div>
          <div class="d_colspan1 border_1">data1-2</div>
          <div class="d_colspan1 border_1">data1-3</div>
          <div class="d_colspan1 border_1">data1-4</div>
          <div class="d_colspan1 border_1_2">data1-5</div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan2 border_1_3_4">data2-1</div>
          <div class="d_colspan1 border_1_3">data2-3</div>
          <div class="d_colspan1 border_1_3">data2-4</div>
          <div class="d_colspan1 border_1_2_3">data2-5</div>
        </div>
      </div>
    </div>
          </div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan1 border_3">data1-1</div>
          <div class="d_colspan1 border_3">data1-2</div>
          <div class="d_colspan1 border_3">data1-3</div>
          <div class="d_colspan1 border_3">data1-4</div>
          <div class="d_colspan1 border_3">data1-5</div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan2 border_0">data2-1</div>
          <div class="d_colspan1 border_0">data2-3</div>
          <div class="d_colspan1 border_0">data2-4</div>
          <div class="d_colspan1 border_0">data2-5</div>
        </div>
      </div>
    </div>
</div>

</body>
</html>

CSS

CSSはこのようにしています。

copy

  .d_table {
    width: 100%;
    display: table;
    border-collapse: collapse;
    text-align: center;
    margin: 0;
  }
  .d_body {
    display: table-row-group;
  }
  .d_row {
    display: table-row;
  }
  .d_colspan1,.d_colspan2,.d_colspan3, .d_id, .d_content  {
    display: table-cell;
    padding: 0px 0px ;
    vertical-align: middle;
    word-break: break-all;
    background-color: aqua;
    hanging-punctuation: allow-end;
    margin: 0;
    width: 20%;
  }
  .d_colspan2 {
    width: 40%;
  }
  .d_colspan3 {
    width: 60%;
  }
  .d_id {
    width: 5%;
  }
  .d_content {
    width: 95%;
  }
  .border_0 {
    border-top: 1px solid #707070;
    border-right: 1px solid #707070;
    border-bottom: 1px solid #707070;
    border-left: 1px solid #707070;
  }
  .border_1 {
    border-top: 0px solid #707070;
    border-right: 1px solid #707070;
    border-bottom: 1px solid #707070;
    border-left: 1px solid #707070;
  }
  .border_2 {
    border-top: 1px solid #707070;
    border-right: 0px solid #707070;
    border-bottom: 1px solid #707070;
    border-left: 1px solid #707070;
  }
  .border_3 {
    border-top: 1px solid #707070;
    border-right: 1px solid #707070;
    border-bottom: 0px solid #707070;
    border-left: 1px solid #707070;
  }
  .border_4 {
    border-top: 1px solid #707070;
    border-right: 1px solid #707070;
    border-bottom: 1px solid #707070;
    border-left: 0px solid #707070;
  }
  .border_1_2 {
    border-top: 0px solid #707070;
    border-right: 0px solid #707070;
    border-bottom: 1px solid #707070;
    border-left: 1px solid #707070;
  }
  .border_1_3 {
    border-top: 0px solid #707070;
    border-right: 1px solid #707070;
    border-bottom: 0px solid #707070;
    border-left: 1px solid #707070;
  }
  .border_1_4 {
    border-top: 0px solid #707070;
    border-right: 1px solid #707070;
    border-bottom: 1px solid #707070;
    border-left: 0px solid #707070;
  }
  .border_2_3 {
    border-top: 1px solid #707070;
    border-right: 0px solid #707070;
    border-bottom: 0px solid #707070;
    border-left: 1px solid #707070;
  }
  .border_2_4 {
    border-top: 1px solid #707070;
    border-right: 0px solid #707070;
    border-bottom: 1px solid #707070;
    border-left: 0px solid #707070;
  }
  .border_3_4 {
    border-top: 1px solid #707070;
    border-right: 1px solid #707070;
    border-bottom: 0px solid #707070;
    border-left: 0px solid #707070;
  }
  .border_1_4 {
    border-top: 0px solid #707070;
    border-right: 1px solid #707070;
    border-bottom: 1px solid #707070;
    border-left: 0px solid #707070;
  }
  .border_1_2_3 {
    border-top: 0px solid #707070;
    border-right: 0px solid #707070;
    border-bottom: 0px solid #707070;
    border-left: 1px solid #707070;
  }
  .border_1_2_4 {
    border-top: 0px solid #707070;
    border-right: 0px solid #707070;
    border-bottom: 1px solid #707070;
    border-left: 0px solid #707070;
  }
  .border_1_3_4 {
    border-top: 0px solid #707070;
    border-right: 1px solid #707070;
    border-bottom: 0px solid #707070;
    border-left: 0px solid #707070;
  }
  .border_1_2_3_4 {
    border-top: 0px solid #707070;
    border-right: 0px solid #707070;
    border-bottom: 0px solid #707070;
    border-left: 0px solid #707070;
  }

  table {
    border-collapse: collapse;
  }

  th, td {

    border: 1px solid rgb(140 140 140);
  }

ブラウザで開いてみた

この表を画像化してみます。

最初のコード

copy

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="./styles/style4.css" rel="stylesheet" />

</head>
<body>

  <br>
  <a href="" id="shot" download="htmlimage.png">画像をダウンロード</a>
  <br>

<div id="htmlcanvas">
    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_id border_3">1</div>
          <div class="d_content border_3">
    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan1 border_1_4">data1-1</div>
          <div class="d_colspan1 border_1">data1-2</div>
          <div class="d_colspan1 border_1">data1-3</div>
          <div class="d_colspan1 border_1">data1-4</div>
          <div class="d_colspan1 border_1_2">data1-5</div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan2 border_1_3_4">data2-1</div>
          <div class="d_colspan1 border_1_3">data2-3</div>
          <div class="d_colspan1 border_1_3">data2-4</div>
          <div class="d_colspan1 border_1_2_3">data2-5</div>
        </div>
      </div>
    </div>
          </div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_id border_3">2</div>
          <div class="d_content border_3">

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan1 border_1_4">data1-1</div>
          <div class="d_colspan1 border_1">data1-2</div>
          <div class="d_colspan1 border_1">data1-3</div>
          <div class="d_colspan1 border_1">data1-4</div>
          <div class="d_colspan1 border_1_2">data1-5</div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan2 border_1_3_4">data2-1</div>
          <div class="d_colspan1 border_1_3">data2-3</div>
          <div class="d_colspan1 border_1_3">data2-4</div>
          <div class="d_colspan1 border_1_2_3">data2-5</div>
        </div>
      </div>
    </div>
          </div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan1 border_3">data1-1</div>
          <div class="d_colspan1 border_3">data1-2</div>
          <div class="d_colspan1 border_3">data1-3</div>
          <div class="d_colspan1 border_3">data1-4</div>
          <div class="d_colspan1 border_3">data1-5</div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan2 border_0">data2-1</div>
          <div class="d_colspan1 border_0">data2-3</div>
          <div class="d_colspan1 border_0">data2-4</div>
          <div class="d_colspan1 border_0">data2-5</div>
        </div>
      </div>
    </div>
</div>

  <script src="./html2canvas.js"></script>
  <script>

    window.onload = function(){
      // body の最後に表の画像を追加します
      html2canvas(document.body,{
          onrendered: function(canvas){
          document.body.appendChild(canvas);
        }
      });

      // クリックで画像をダウンロードします
      html2canvas(document.body,{
          onrendered: function(canvas){
          var imgShot = canvas.toDataURL();
          document.getElementById("shot").href = imgShot;
        }
      });
    }

  </script>

</body>
</html>

ブラウザで開いてみた

期待した画像の表示はなく、ダウロードもファイルが異常になっているようです。

html2canvas の戻り値は Promise

使った html2canvas.js はバージョン 1.4.1 です。これをエディター(メモ帳)で開いて中を見てみます。

最後の方に、html2canvas 関数があります。その中身は、戻り値が renderElement 関数の戻り値になっています。

    var html2canvas = function (element, options) {
        if (options === void 0) { options = {}; }
        return renderElement(element, options);
    };

html2canvas 関数のすぐ下に renderElement 関数があります。その中身を見てみますと、戻り値が __awaiter の戻り値となっています。

    var renderElement = function (element, opts) { return __awaiter(void 0, void 0, void 0, function () {
        var ownerDocument, defaultView, resourceOptions, contextOptions, windowOptions, windowBounds, context, foreignObjectRendering, cloneOptions, documentCloner, clonedElement, container, _a, width, height, left, top, backgroundColor, renderOptions, canvas, renderer, root, renderer;
        var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
        return __generator(this, function (_u) {
               //省略
    }); };

__awaiter 関数は先頭行に近いところにありました。戻り値を見てみますとPromise とあります。

    function __awaiter(thisArg, _arguments, P, generator) {
        function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
        return new (P || (P = Promise))(function (resolve, reject) {
            function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
            function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
            function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
            step((generator = generator.apply(thisArg, _arguments || [])).next());
        });
    }

失敗した次の書き方は、html2canvas.js 1.0.0 未満では動作するようです。今回は html2canvas.js 1.4.1 を使いますので書き直します。

  <script>

    window.onload = function(){
      // body の最後に表の画像を追加します
      html2canvas(document.body,{
          onrendered: function(canvas){
          document.body.appendChild(canvas);
        }
      });

      // クリックで画像をダウンロードします
      html2canvas(document.body,{
          onrendered: function(canvas){
          var imgShot = canvas.toDataURL();
          document.getElementById("shot").href = imgShot;
        }
      });
    }

  </script>

html2canvas.js 0.4.1 を見てみますと、次のようになっていますので失敗した上の構文は正しく動作します。

window.html2canvas = function(elements, opts) {
  elements = (elements.length) ? elements : [elements];
  var queue,
  canvas,
  options = {
             //省略
  };
             //省略
    canvas = _html2canvas.Renderer( queue, options );

    if (typeof options.onrendered === "function") {
      options.onrendered( canvas );
    }
             //省略
  };

次のように戻り値が Promise であるときの構文にします。

  <script type="text/javascript">

    window.onload = function(){
      // body の最後に表の画像を追加します
        html2canvas(document.body).then(function(canvas) {
            document.body.appendChild(canvas);
        });


      // クリックで画像をダウンロードします 
        html2canvas(document.body).then(function(canvas) {
            var imgShot = canvas.toDataURL();
            document.getElementById("shot").href = imgShot;
        });

    }

  </script>

書き直し後の HTML

copy

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="./styles/style4.css" rel="stylesheet" />

</head>
<body>

  <br>
  <a href="" id="shot" download="htmlimage.png">画像をダウンロード</a>
  <br>

<div id="htmlcanvas">
    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_id border_3">1</div>
          <div class="d_content border_3">
    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan1 border_1_4">data1-1</div>
          <div class="d_colspan1 border_1">data1-2</div>
          <div class="d_colspan1 border_1">data1-3</div>
          <div class="d_colspan1 border_1">data1-4</div>
          <div class="d_colspan1 border_1_2">data1-5</div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan2 border_1_3_4">data2-1</div>
          <div class="d_colspan1 border_1_3">data2-3</div>
          <div class="d_colspan1 border_1_3">data2-4</div>
          <div class="d_colspan1 border_1_2_3">data2-5</div>
        </div>
      </div>
    </div>
          </div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_id border_3">2</div>
          <div class="d_content border_3">

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan1 border_1_4">data1-1</div>
          <div class="d_colspan1 border_1">data1-2</div>
          <div class="d_colspan1 border_1">data1-3</div>
          <div class="d_colspan1 border_1">data1-4</div>
          <div class="d_colspan1 border_1_2">data1-5</div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan2 border_1_3_4">data2-1</div>
          <div class="d_colspan1 border_1_3">data2-3</div>
          <div class="d_colspan1 border_1_3">data2-4</div>
          <div class="d_colspan1 border_1_2_3">data2-5</div>
        </div>
      </div>
    </div>
          </div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan1 border_3">data1-1</div>
          <div class="d_colspan1 border_3">data1-2</div>
          <div class="d_colspan1 border_3">data1-3</div>
          <div class="d_colspan1 border_3">data1-4</div>
          <div class="d_colspan1 border_3">data1-5</div>
        </div>
      </div>
    </div>

    <div class="d_table">
      <div class="d_body">
        <div class="d_row">
          <div class="d_colspan2 border_0">data2-1</div>
          <div class="d_colspan1 border_0">data2-3</div>
          <div class="d_colspan1 border_0">data2-4</div>
          <div class="d_colspan1 border_0">data2-5</div>
        </div>
      </div>
    </div>
</div>

  <script type="text/javascript" src="./html2canvas141.js"></script>
  <script type="text/javascript">

    window.onload = function(){
      // body の最後に表の画像を追加します
        html2canvas(document.body).then(function(canvas) {
            document.body.appendChild(canvas);
        });


      // クリックで画像をダウンロードします 
        html2canvas(document.body).then(function(canvas) {
            var imgShot = canvas.toDataURL();
            document.getElementById("shot").href = imgShot;
        });

    }

  </script>

</body>
</html>

修正 HTML を開く

先ほどと違い開いた時には下に画像が表示されています。また、ダウンロードは警告も出ずに行われました。

ダウンロードした画像

表現がオリジナルと違ったところがありますが、ここまで再現できれば素晴らしいです。

この件はここまでとします。