バックエンドの技術を使わずにウェブのフロントエンドだけで、つまり HTML と JavaScript だけで QR コードリーダーを作ることに挑戦してみました:

まずは完成品で実際の挙動を確認ください。PC またはスマホで(できればスマホで)こちらの URL にアクセスしてください:
https://dotnsf.github.io/web_qrcode/

↑Github Pages で公開しているページなので、フロントエンドだけで作られていることがわかると思います。クロスオリジンな AJAX なども使ってないので、興味ある方はソースコードも確認してください。
「ファイルを選択」ボタンをクリックします。スマホの場合はカメラが起動するので QR コードを撮影してください。また PC の場合は画像選択ボックスが表示されるので、QR コードが撮影された画像を選択してください。
すると画面下部に画像が表示され、同時に画面内の QR コードが(存在していれば)解析され、解析結果の文字列データが表示されます:

フロントエンドだけでここまでできるので、後は例えばこの結果が URL 文字列になっていれば、そのアドレスのページを開くこともできますし、テキストデータだけを読み取った上でバックエンドサーバーにデータを送信する、といったサーバーとの連携処理も容易に可能です。
【ソースコードの解説】
ソースコードはこちらで MIT ライセンスで公開しています(GitHub のウェブホスティングである Github Pages を使ってそのままサービスも公開しています):
https://github.com/dotnsf/web_qrcode
特にサービスの肝である index.html ファイルはこちらから参照いただけます。実質的にこの1ファイルだけでカメラ起動から撮影、QR コード解析まで実現しています:
https://github.com/dotnsf/web_qrcode/blob/main/index.html
まずスマホでカメラを起動するボタンの部分は以下の HTML コードで実現しています(PC ブラウザでこのボタンをクリックすると、保存済みの画像ファイルを選択するダイアログが表示されます):
type="file" で accept="image/*" capture="camera" 属性のついた <input> タグをクリックすると、カメラ内蔵機種の場合はそのカメラが起動し、撮影した画像を選択したことになります。(PCのなど)カメラ内蔵機種でない場合は普通にファイル選択画面になりますが、accept 属性により画像ファイルしか選択できません(要するにここで QR コードを撮影するか、QR コードが撮影された画像を選択することを想定しています)。
また jQuery を使った JavaScript によって以下の処理が画面ロード直後に実行されています:
HTML 内下部に設置された <canvas> と、そのコンテキストを取得します(この <canvas> に画像が描画されることになりますが、その処理は後述します)。また上述の <input> タグの値が変化した時(=カメラで撮影するか、画像ファイルが選択された時)に selectReadFile() 関数が実行されるよう定義されています:
したがってカメラで撮影するか画像ファイルを選択すると、以下の JavaScript コードが実行されます:
HTML5 の Filesystem API を使って撮影された画像ファイルを読み込み、その内容を上述で取得した <canvas> に描画するための関数 readDrawImg() が実行されます。つまり撮影した画像が画面内にも表示されるようになります。
readDrawImg() 関数および、この中で実行される多くの関数の中身は以下のようになっています:
まず readImg() 関数の中で Filesystem API を用いて画像ファイルを読み込み、その結果を Image オブジェクトの src 属性に代入します(これで Image オブジェクトを drawImgOnCav() 関数で <canvas> に表示すると読み込んだ画像が表示されます)。またこの結果から画像の幅および高さを取得して、画面内に元画像のサイズとして表示されます。
drawImgOnCav() 関数の最後に checkQRCode() 関数が実行されています。画像の <canvas> への描画終了後にこの関数が実行され、画像に QR コードが写っていたらその内容を解析して、データを取り出します:
具体的にはコンテキスト変数(ctx)から getImageData() 関数でイメージデータを取得し、その結果を jsQR を使って解析します。ややこしそうな QR コードの解析部分は実はライブラリによって一瞬で実現できてしまっています。jSQR バンザイ!
正しく QR コードが解析できた場合は解析結果の文字列(code.data)を alert() 関数で画面に表示し、解析できなかった場合は "No QR Code found." と表示される、という一連の処理が定義されています。
今回の処理では QR コードが正しく解析でした場合はそのまま表示しているだけですが、これが URL であればロケーションに指定してジャンプしてもよし、テキストなどのデータであればバックエンドの API にポストしてもよし、というわけで取り出したデータの使いみちは alert() の代わりに記述することで、自由に応用できると思います。
というわけで、Filesystem API と jsQR と <input> タグのカメラ拡張を使うことで、ウェブのフロントエンド技術だけで QR コードリーダーが実現できました。

まずは完成品で実際の挙動を確認ください。PC またはスマホで(できればスマホで)こちらの URL にアクセスしてください:
https://dotnsf.github.io/web_qrcode/

↑Github Pages で公開しているページなので、フロントエンドだけで作られていることがわかると思います。クロスオリジンな AJAX なども使ってないので、興味ある方はソースコードも確認してください。
「ファイルを選択」ボタンをクリックします。スマホの場合はカメラが起動するので QR コードを撮影してください。また PC の場合は画像選択ボックスが表示されるので、QR コードが撮影された画像を選択してください。
すると画面下部に画像が表示され、同時に画面内の QR コードが(存在していれば)解析され、解析結果の文字列データが表示されます:

フロントエンドだけでここまでできるので、後は例えばこの結果が URL 文字列になっていれば、そのアドレスのページを開くこともできますし、テキストデータだけを読み取った上でバックエンドサーバーにデータを送信する、といったサーバーとの連携処理も容易に可能です。
【ソースコードの解説】
ソースコードはこちらで MIT ライセンスで公開しています(GitHub のウェブホスティングである Github Pages を使ってそのままサービスも公開しています):
https://github.com/dotnsf/web_qrcode
特にサービスの肝である index.html ファイルはこちらから参照いただけます。実質的にこの1ファイルだけでカメラ起動から撮影、QR コード解析まで実現しています:
https://github.com/dotnsf/web_qrcode/blob/main/index.html
まずスマホでカメラを起動するボタンの部分は以下の HTML コードで実現しています(PC ブラウザでこのボタンをクリックすると、保存済みの画像ファイルを選択するダイアログが表示されます):
<input type="file" accept="image/*" capture="camera" name="file" id="file-image"/>
type="file" で accept="image/*" capture="camera" 属性のついた <input> タグをクリックすると、カメラ内蔵機種の場合はそのカメラが起動し、撮影した画像を選択したことになります。(PCのなど)カメラ内蔵機種でない場合は普通にファイル選択画面になりますが、accept 属性により画像ファイルしか選択できません(要するにここで QR コードを撮影するか、QR コードが撮影された画像を選択することを想定しています)。
また jQuery を使った JavaScript によって以下の処理が画面ロード直後に実行されています:
var canvas = null; var ctx = null; $(function(){ canvas = document.getElementById( 'mycanvas' ); ctx = canvas.getContext( '2d' ); var file_image = document.getElementById( 'file-image' ); file_image.addEventListener( 'change', selectReadFile, false ); });
HTML 内下部に設置された <canvas> と、そのコンテキストを取得します(この <canvas> に画像が描画されることになりますが、その処理は後述します)。また上述の <input> タグの値が変化した時(=カメラで撮影するか、画像ファイルが選択された時)に selectReadFile() 関数が実行されるよう定義されています:
<div> <canvas id="mycanvas"></canvas> </div>
したがってカメラで撮影するか画像ファイルを選択すると、以下の JavaScript コードが実行されます:
function selectReadFile( e ){ var file = e.target.files; var reader = new FileReader(); reader.onload = function(){ readDrawImg( reader, canvas, 0, 0 ); } reader.readAsDataURL( file[0] ); }
HTML5 の Filesystem API を使って撮影された画像ファイルを読み込み、その内容を上述で取得した <canvas> に描画するための関数 readDrawImg() が実行されます。つまり撮影した画像が画面内にも表示されるようになります。
readDrawImg() 関数および、この中で実行される多くの関数の中身は以下のようになっています:
function readDrawImg( reader, canvas, x, y ){ var img = readImg( reader ); img.onload = function(){ var w = img.width; var h = img.height; printWidthHeight( 'src-width-height', true, w, h ); // resize var resize = resizeWidthHeight( 1024, w, h ); printWidthHeight( 'dst-width-height', resize.flag, resize.w, resize.h ); drawImgOnCav( canvas, img, x, y, resize.w, resize.h ); } } function readImg( reader ){ var result_dataURL = reader.result; var img = new Image(); img.src = result_dataURL; return img; } function drawImgOnCav( canvas, img, x, y, w, h ){ canvas.width = w; canvas.height = h; ctx.drawImage( img, x, y, w, h ); checkQRCode(); } function resizeWidthHeight( target_length_px, w0, h0 ){ var length = Math.max( w0, h0 ); if( length <= target_length_px ){ return({ flag: false, w: w0, h: h0 }); } var w1; var h1; if( w0 >= h0 ){ w1 = target_length_px; h1 = h0 * target_length_px / w0; }else{ w1 = w0 * target_length_px / h0; h1 = target_length_px; } return({ flag: true, w: parseInt( w1 ), h: parseInt( h1 ) }); } function printWidthHeight( width_height_id, flag, w, h ){ var wh = document.getElementById( width_height_id ); wh.innerHTML = '幅: ' + w + ', 高さ: ' + h; }
まず readImg() 関数の中で Filesystem API を用いて画像ファイルを読み込み、その結果を Image オブジェクトの src 属性に代入します(これで Image オブジェクトを drawImgOnCav() 関数で <canvas> に表示すると読み込んだ画像が表示されます)。またこの結果から画像の幅および高さを取得して、画面内に元画像のサイズとして表示されます。
drawImgOnCav() 関数の最後に checkQRCode() 関数が実行されています。画像の <canvas> への描画終了後にこの関数が実行され、画像に QR コードが写っていたらその内容を解析して、データを取り出します:
function checkQRCode(){ var imageData = ctx.getImageData( 0, 0, canvas.width, canvas.height ); var code = jsQR( imageData.data, canvas.width, canvas.height ); if( code ){ //console.log( code ); alert( code.data ); }else{ alert( "No QR Code found." ); } }
具体的にはコンテキスト変数(ctx)から getImageData() 関数でイメージデータを取得し、その結果を jsQR を使って解析します。ややこしそうな QR コードの解析部分は実はライブラリによって一瞬で実現できてしまっています。jSQR バンザイ!
正しく QR コードが解析できた場合は解析結果の文字列(code.data)を alert() 関数で画面に表示し、解析できなかった場合は "No QR Code found." と表示される、という一連の処理が定義されています。
今回の処理では QR コードが正しく解析でした場合はそのまま表示しているだけですが、これが URL であればロケーションに指定してジャンプしてもよし、テキストなどのデータであればバックエンドの API にポストしてもよし、というわけで取り出したデータの使いみちは alert() の代わりに記述することで、自由に応用できると思います。
というわけで、Filesystem API と jsQR と <input> タグのカメラ拡張を使うことで、ウェブのフロントエンド技術だけで QR コードリーダーが実現できました。
コメント
コメント一覧 (16)
教えていただきたい事があります。
上記のソースでは。
カメラの撮影条件が悪くてQRコードが読み込めない時は、
「No QR Code found.」を表示して、初めの画面に戻ってしまいます。
QRコードを再撮影するには。
再び「ファイルを選択」ボタンを押して、撮影モードに入る必要があります。
そこで私の希望ですが
「No QR Code found.」を表示した後、自動で撮影モードに入る ようにできないでしょうか?
私なりに考えて「ファイルを選択」ボタンの
<input type="file" accept="image/*" capture="camera" name="file" id="file-image"/> を
javascriptでクリックできないかと思い。
alert( "No QR Code found." ); の次行に
document.getElementById("file-image").click(); を書いてみましたが、うまくいきませんでした。
暇なときにでも御返事がいただければ幸いです。
この <input type="file"> を使ってカメラ撮影する場合、『「ファイルを選択」ボタンを押す』というアクションがないとカメラが起動しない仕様なんですよね。なので、この方法だとあまり自由度高くカスタマイズできないのです。
どちらかというと、こちらのサンプルを使ったほうが(自由度も高いので)やりたいことができるんじゃないかな、と思いました。カメラを動画モードで利用しながら QR コードを読み取り、正しい QR コードとして読み取れた場合だけ alert で表示するようにしています(読み取れない場合はそのまま QR コードを探し続ける):
https://github.com/dotnsf/videosize/blob/main/docs/index.html
リンク先のものを試してみます。
これをDLして、自分のパソコン(xamppをインストルー済み)に入れて試したところ。全く動きません。
xamppサーバーに、SSLサーバの設定が必要なのでしょうか?
他のサイトの物は。カメラを動画モードで利用しながら QR コードを読み取り、正しい QR コードとして読み取れた場合だけ表示する
という仕様でした。
これらは、自宅のxamppサーバーが入っているパソコンに入れて試すと全く動きませんでした。
私の、xamppの設定のどこかが間違っているのかもしれません。
調べたけれど全くわからなくて困っています。
このHPの、「ファイルを選択」ボタンを押して撮影モードに入るのは、xamppサーバーに入れても、問題なく動きました。
そんなわけで、ここに質問させていただきました。
QRコードを読みこむアプリは、会社の出荷管理に使いたいと思っています。
具体的には、会社のパソコンにxamppサーバーとQRコード読込のHTMLファイルを入れておいて。
タブレットでQRコードを読み込んで、読み込んだQRデータをサーバーに保存したい と構想しています。
さらに試行錯誤して試してみます。
質問があります。
https://github.com/dotnsf/videosize/blob/main/docs/index.html をサーバーに保存して使用する場合、
サーバーに、index.htmlの他にファイルが必要なのでしょうか?
もし必要なファイルを教えていただければ、
それをどこかの無料サーバーにUPして試してみたいです。
たいへん不躾な質問ではありますが。
このindex.html はどこかのサーバーに保存して、デモとして使えるのはありますか?
自分のスマホで動くか試してみたいです。
↑こちらで動くようにしました。最初にカメラ許可のダイアログが出るので「許可」してください。
HTTP サーバーなしで(URL が file:/// で始まるローカルファイルとして)開く場合は動かないと思います。
試してみます
結果は
私のスマホでは全く動きませんでした。
新しいipadでは、向こう側のカメラではなく、手前側のカメラが撮影開始して、QRコードを解読しました。
新しいノートパソコンでは、手前側のカメラ(ノートパソコンにはこのカメラしか付いてない)が撮影開始して、QRコードを解読しました。
どうも私のスマホは古いので動かないみたいです。
完全に諦めました。
今回はありがとうございました。
このHPの情報を元に、QRコードを読み取りするWEBアプリが作れました。
読み込んだデータをサーバーに保存できる仕様に改造して使用してます。
こちらで紹介していただいた方法だと、「少し古いスマホ+SSLが入ってないXAMPP」でも動くので、大変重宝しております。
ところで、
CODE39バーコードを読みたいという要望がでてきました。
こちらのQRコードリーダーを改造して、「少し古いスマホ+SSLが入ってないXAMPP」で
CODE39バーコードが読めるようにできないでしょうか?
↑作り直してみました。CODE39 バーコードと QR コードの両方を読み取れると思います。
ためしてみます。
さっそく試してみました。
https://dotnsf.github.io/videosize/ からDLしたindex.html を、「SSLが入ってないXAMPP」に入れると
全く動きませんでした。
https://github.com/dotnsf/web_qrcode で公開されているindex.html では
quagga.js を使っていないので、「SSLが入ってないXAMPP」では動くのでしょう。
https://dotnsf.github.io/videosize/ を新しいIpadで開くと。QRコードは読めました、CODE39バーコードは読めませんでした。
https://dotnsf.github.io/videosize/ を古いスマホでで開くと。カメラが起動しませんでした
https://github.com/dotnsf/web_qrcode で公開されているindex.html では
読み込みのボタンを押す必要はあるけれど。
「少し古いスマホ+SSLが入ってないXAMPP」でも動くので、大変重宝しているのですが。
バーコードよりも複雑なQRコードが、「少し古いスマホ+SSLが入ってないXAMPP」でも読めるので。
少し改造するだけで、バーコードも読めるかと思っていましたが。私の考えが甘かったようです。