こんにちは!話題のELDEN RINGを買ったはいいけどあまり遊べていない竹村です。
現場業務にて、JavaScriptのフレームワークであるKonva.jsとjsPDFを使って、画面に描画されているstage要素をPDF化しようとしたところ、stage要素のスケールも含めてPDF化されてしまい、stage全体がPDF化されない現象が起こったため、その解決方法を書いていきたいと思います。
尚、環境はVue2系となります。
※stageはマウスホイール操作で拡大縮小できるようにしていた為、画面上に描画されているスケール通りにPDFが生成されてしまっていました。
Konva.jsとは?
Konva.jsはJavaScriptのフレームワークで、divタグ内のcanvas要素を作成し、図形の生成や編集、画像の配置等ができます。stageという最下層のレイヤーの上に、複数のlayerを追加して画面描画できます。
座標やスケールを操作する事で、stage上の要素を拡大縮小したり、html2Canvasを使用する事で画像化できたりと、とても汎用性の高いフレームワークとなっています。
jsPDFとは?
JavaScriptのライブラリです。生成されたjsPDFインスタンスに対し、テキストや図形、画像等を追加し、簡単にPDFを作成してくれます。
また、jsPDFインスタンスはarraybufferやblobといった形で出力できるので、base64形式にエンコードしてDBに保存もできたりします。
英語ですが公式ドキュメントも充実していますので、使い勝手の良いライブラリとなっています。
結論から言うと
stage要素全体がPDF化されない件について結論から述べますと、stageのscaleとpositionを初期化する事で解決しました。
まず、pdf化させる関数内でstageを取得します。(関数の引数はselfとしています)
var stage = self.$refs["stage"];
var getStage = stage.getStage();
$refs[“stage”]で取得したstage要素から、getStage()することでstage要素を取得できます。
(最初、$refsで取得した段階でstageが取れたと思っていたのですが、stageをコンソールにログ出力したところ、更にgetStage()する必要があるとわかりました。ややこしい。。。)
stage要素を取得できたところで、scaleとpositionを初期化してあげます。
getStage.setScale({ x: 1, y: 1 });
getStage.setPosition({ x: 0, y: 0 });
scaleは拡大縮小率なので、等倍である1に設定します。
positionは、画面上でstage要素をドラッグで動かしている場合があるので、座標位置を0に戻してあげます。
これで、stage要素を等倍かつ正しくPDF化させることができました。
PDF化関数の全体的なソースは、こんな感じです。
function downloadPdf(self) {
//stageを取得
var stage = self.$refs["stage"];
var getStage = stage.getStage();
//pdfサイズとなる縦横の幅を取得
var width = getStage.attrs.width;
var height = getStage.attrs.height;
//pdfインスタンスを生成
var pdf = new jsPDF({
orientation: "l",
unit: "px"
format: [width, height],
compress: true,
});
//現状のscaleとpositionを取得
var scale = getStage.getScale();
var position = getStage.getPosition();
//scaleとpositionを初期化
getStage.setScale({ x: 1, y: 1 });
getStage.setPosition({ x: 0, y: 0 });
//pdf化処理
var output = getStage.toImage({
callback(image) {
//pdfインスタンスにimageを追加
pdf.addImage(image, 10, 10, width, height);
//scaleとpositionを元に戻す
getStage.setScale(scale);
getStage.setPosition(position);
//pdfダウンロード
pdf.save("hoge.pdf");
},
mimeType: "image/png",
x: 10,
y: 10,
width: width,
height: height,
});
}
解説
7、8行目で縦幅と横幅を取得していますが、PDF出力させたい幅を固定値で入れても問題ありません。例として、ここではstageそのものの縦横としています。
11行目で、jsPDFインスタンスを作成しています。作成する際、引数をいくつか設定しています。
orientation:pdfを出力する際の向き(縦か横か)を指定できます。ここでは、設定値に”l”(landscapeのl)を指定している為、出力される際は横向きになります。
unit:出力する際の単位を指定します。デフォルトはpxなので、わざわざ書かなくても大丈夫です。
format:縦横幅のサイズです。a4やb5などといった指定の仕方もできますし、今回のように配列で直接指定することもできます。
compress:pdfを出力する際、圧縮するかどうかをtrueかfalseで指定できます。自分が試したときは、指定しないとファイルサイズがとんでもないことになったので、trueにすることをお勧めします。。。
19、20行目では、現状のscaleとpositionを取得しています。
一旦保持しておき、これらを初期化した後に33、34行目で元の値に戻しています。
実際のPDF化している部分では、Konva.jsのStageが持つtoImage()メソッドを使用して、stage要素を一旦画像にしたうえで(callback関数の引数imageが画像です)、pdfインスタンスのaddImage()メソッドに渡しています。
ポイントとしては、toImage()メソッドの第一引数がcallback関数の為、その中でpdfインスタンスにimageを渡し、save処理していることです。
終わりに
いかがでしたでしょうか。スケールとポジションを一旦初期化している為、画面上は一瞬stage要素の大きさが変化してしまいますが、理想の形でPDFが作成されるのであれば許容範囲といったところでしょうか。
それでは、皆さんも良きPDFライフを!