動作環境
Angular8ngx-kjua1.6.0
画面
コード
.html部分
<canvas id="decorated_qr" width="{{canvas_size}}" height="{{canvas_size}}"></canvas>
<br>
<button (click)="onDownload()">Download</button>
<br>
<button (click)="onRefresh()">Refresh</button>
<br>
<div hidden="hidden" >
<ngx-kjua [render]="'image'" [size]="qr_gen_size"
[text]="qr_text" [mode]="'label'" [label]="label_text"
[mSize]="10" [fontcolor]="label_color">
</ngx-kjua>
</div>
<router-outlet></router-outlet>
.ts部分
import { Component,OnInit,AfterViewInit } from '@angular/core';
import {saveAs} from "file-saver";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit{
title = 'qr-demo';
constructor() { }
qr_gen_size = 300;
fontsize = 14;
margin = 24;
fontweight = "bold";
offset = this.fontsize+this.margin;
canvas_size = this.qr_gen_size+this.offset*2;
qr_text = "hello, QR Code!!";
decoration_text = "MAY THE FORCE BE WITH YOU ";
label_text = "SW";
label_color = "blue";
ngAfterViewInit() {
this.addDecoration(this.decoration_text);
}
onRefresh() {
this.addDecoration(this.decoration_text);
}
addDecoration(s:string) {
let head_offset = this.fontsize;
let deco = s.repeat(5);
let img = document.getElementsByTagName('img');
let canvas = document.getElementById('decorated_qr');
let context = (canvas as any).getContext("2d");
context.drawImage(img[0], 0, 0, this.qr_gen_size,this.qr_gen_size,
this.offset, this.offset,
this.qr_gen_size,this.qr_gen_size);
context.fillStyle = "black";
context.font = this.fontweight+' '+this.fontsize+'px sans-serif';
let region = new Path2D();
region.rect(0, 0,
this.qr_gen_size+this.offset*2-this.fontsize,
this.offset);
context.save();
context.beginPath(); // clipping
context.clip(region, "nonzero");
context.fillText(deco, head_offset,this.fontsize);
context.restore();
context.save();
context.translate(this.qr_gen_size+this.offset*2,0);
region.rect(0, 0,
this.qr_gen_size+this.offset*2-this.fontsize,
this.offset);
context.rotate((Math.PI/180)*90);
context.beginPath(); // clipping
context.clip(region, "nonzero");
context.fillText(deco, head_offset,this.fontsize);
context.restore();
context.save();
context.translate(this.qr_gen_size+this.offset*2,
this.qr_gen_size+this.offset*2);
context.rotate((Math.PI/180)*180);
context.beginPath(); // clipping
context.clip(region, "nonzero");
context.fillText(deco, head_offset,this.fontsize);
context.restore();
context.save();
context.translate(0,this.qr_gen_size+this.offset*2);
context.rotate((Math.PI/180)*270);
context.beginPath(); // clipping
context.clip(region, "nonzero");
context.fillText(deco, head_offset,this.fontsize);
context.restore();
}
onDownload() {
let canvas = document.getElementById('decorated_qr');
(canvas as any).toBlob(function(blob) {
saveAs(blob, "qr.png");
});
}
}
コードの説明
ngx-kjuaで生成したQRコードのimageをcanvasに転送後、装飾部分を追加で描画しています。
- ngx-kjuaのQRコード生成をimageで行う。
- imageを document.getElementsByTagName('img')で取得する(取得結果は配列になる)。
- canvasをdocument.getElementById('decorated_qr')で取得する。
- imageをcanvasに転送する。この時、装飾用のマージン領域を確保する。
- 装飾用の文字列は、translateとrorateで位置を調整して4回描画する。この時、save()とrestore()でオリジンを戻す。
- 装飾用の文字列は、余分に描画したくない部分をclip()でマスクする。
エラー対策
(canvas as any).getContext("2d")の部分はERROR in src/app/app.component.ts(xx,24): error TS2339: Property 'getContext' does not exist on type 'HTMLElement'.
といったエラーが出力されるので、canvasをanyにキャストしています。
let context = (canvas as any).getContext("2d");
また、(canvas as any).toBlobは
ERROR in src/app/app.component.ts(xx,10): error TS2339: Property 'toBlob' does not exist on type 'HTMLElement'
といったエラーが出力されるので、canvasをanyにキャストしています。
(canvas as any).toBlob(function(blob) {
saveAs(blob, "qr.png");
});