AngularでQRコードの周りに文字の装飾を付ける

2019年12月21日土曜日

Angular Linux

t f B! P L
Angular+ngx-kjuaで、QRコードの周囲を囲むように装飾の文字列を配置するコードのサンプルです。

動作環境

Angular8
ngx-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()でマスクする。
生成した装飾付きQRコードは、Downloadボタンでpngとしてダウンロードします。ダウンロード部分は、FileSaver.js(2.0.2)を利用しました。

エラー対策

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

うまく動作しないタイミングがある

AfterViewInitでimageからcanvasへの転送を行っていますが、たまにimageの描画前にcanvasへの転送が始まってしまう場合があるようです。仕方ないので、その場合はRefreshボタンで手動生成するようにしています。

このブログを検索

QooQ