Sibainu Relax Room

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

JavaScript html2canvas のオプション

神社の狛犬と競い合うのが好きなようです。僕の方がカッコいいだろうという顔している柴犬です。

概要

HTML を html2canvas で画像化するとき、ブラウザのウィンドウの幅の都合により表が折り返しになるときがあると思います。

折り返しが生じないようにできるのか考えてみました。

html2canvas のコードを見てみることにします。

windowWidth、widthというオプションがありますので色々試してみました。

その記録です。

html2canvas のオプション

オプションにある windowWidth windowHeight はHTMLのウィンドの大きさを指定できるようです。

指定がなければ、ブラウザの今ある表示の大きさを使うようです。

これを指定してどうなるか試してみます。

オプションの記述は、オブジェクトで渡します。

7755行
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;
        //省略
7779行
    windowOptions = {
      windowWidth: (_f = opts.windowWidth) !== null &&
                    _f !== void 0 ? _f : defaultView.innerWidth,
      windowHeight: (_g = opts.windowHeight) !== null &&
                     _g !== void 0 ? _g : defaultView.innerHeight,
      scrollX: (_h = opts.scrollX) !== null &&
                _h !== void 0 ? _h :defaultView.pageXOffset,
      scrollY: (_j = opts.scrollY) !== null &&
                _j !== void 0 ? _j : defaultView.pageYOffset
    };
    windowBounds = new Bounds(windowOptions.scrollX,
                windowOptions.scrollY,
                windowOptions.windowWidth,
                windowOptions.windowHeight);
    context = new Context(contextOptions, windowBounds);
    foreignObjectRendering = (_k = opts.foreignObjectRendering) !== null &&
                              _k !== void 0 ? _k : false;
    cloneOptions = {
      allowTaint: (_l = opts.allowTaint) !== null &&
                   _l !== void 0 ? _l : false,
      onclone: opts.onclone,
      ignoreElements: opts.ignoreElements,
      inlineImages: foreignObjectRendering,
      copyStyles: foreignObjectRendering
    };
    context.logger.debug("Starting document clone with size " + windowBounds.width + "x" + windowBounds.height + " scrolled to " + -windowBounds.left + "," + -windowBounds.top);
    documentCloner = new DocumentCloner(context, element, cloneOptions);
    clonedElement = documentCloner.clonedReferenceElement;
        //省略
};

オプションの width height は何に使うのかと調べてみると、canvas の大きさのようです。

これも変化させて試してみます。

7608行
var ForeignObjectRenderer = /** @class */ (function (_super) {
    __extends(ForeignObjectRenderer, _super);
    function ForeignObjectRenderer(context, options) {
      var _this = _super.call(this, context, options) || this;
          _this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
          _this.ctx = _this.canvas.getContext('2d');
          _this.options = options;
          _this.canvas.width = Math.floor(options.width * options.scale);
          _this.canvas.height = Math.floor(options.height * options.scale);
          _this.canvas.style.width = options.width + "px";
          _this.canvas.style.height = options.height + "px";
          _this.ctx.scale(_this.options.scale, _this.options.scale);
          _this.ctx.translate(-options.x, -options.y);
          _this.context.logger.debug("EXPERIMENTAL ForeignObject renderer initialized (" + options.width + "x" + options.height + " at " + options.x + "," + options.y + ") with scale " + options.scale);
            return _this;
   }
        //省略
}(Renderer));

windowWidth:5000

オプション ({windowWidth: 5000}) を指定します。

copy

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

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


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

    }

  </script>

画像ファイル

元のHTMLの表がレスポンシブルですので幅5000pxいっぱいの大きな表になりその画像になりました。

ですので、画像は仮想ウィンドウを作ってそこに再表現するようですので、ブラウザのウィンドウの都合で折り返しがあっても仮想ウィンドウを十分に大きくすれば折り返しのない表の画像が作成できるようです。

windowWidth:5000 width:1000

オプション ({windowWidth: 5000, width:1000}) を指定します。

copy

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

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


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

    }

  </script>

画像ファイル

ウィンドウ幅5000pxの左端1000pxの画像になりました。

ウィンドウ幅のみ指定すれば、それに応じた canvas の大きさになるようです。

作成されたファイル

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

ファイルのプロパティを見ると幅が 5000、1000 と指定したのに 6250、1250 となっています。

それぞれ 1.25 倍になっています。

ForeignObjectRenderer の中で canvas の大きさをオプションの scale を掛けています。

その大きさが 1.25 のようです。

なぜそのような値を掛けているのでしょう。

7608行
var ForeignObjectRenderer = /** @class */ (function (_super) {
    __extends(ForeignObjectRenderer, _super);
    function ForeignObjectRenderer(context, options) {
        //省略
          _this.canvas.width = Math.floor(options.width * options.scale);
          _this.canvas.height = Math.floor(options.height * options.scale);
        //省略

scale の値のセットを見てみます。

オプションで指定があればその値をセットしますが、指定がなければ defaultView.devicePixelRatio をセットしています。

7726行
var renderElement = function (element, opts) { return __awaiter(void 0, void 0, void 0, function () {
        //省略
7779行
    renderOptions = {
      canvas: opts.canvas,
      backgroundColor: backgroundColor,
      scale: (_o = (_m = opts.scale) !== null && _m !== void 0 ? _m : defaultView.devicePixelRatio) !== null && _o !== void 0 ? _o : 1,
        //省略

次のMDNのWEBのHPに defaultView.devicePixelRatio 解説があります。

https://developer.mozilla.org/ja/docs/Web/API/Window/devicePixelRatio
<canvas> の解像度の補正
Retina 画面では <canvas> がぼやけて見えることがあるでしょう。 window.devicePixelRatio を使うことで、鮮明に表示するために必要なピクセル密度を調べます。

とありますので、この場合は 1.25 ということなのでしょう。

試しに{scale: 1.0}としてみます。

copy

  <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);
        });


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

    }

  </script>

ファイルを開いて比較してみます

わたしのPCの画面ではその違いは感じられませんでしたので、オプション{scale: 1.0} で指定するか、次のようデフォルトを 1.0 にしてもいいかも。

7726行
var renderElement = function (element, opts) { return __awaiter(void 0, void 0, void 0, function () {
        //省略
7779行
    renderOptions = {
      canvas: opts.canvas,
      backgroundColor: backgroundColor,
      scale: (_o = (_m = opts.scale) !== null && _m !== void 0 ? _m : 1.0) !== null && _o !== void 0 ? _o : 1,
        //省略

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