バーコードやQRコードによって買い物や検索の仕方が変わりました。世界中どこでも商品を手に取ったら、バーコードやQRコードをスマートフォンの無料アプリで読み取り、最低価格や取扱店を探せます。
ウォルマートやアマゾンはこの流れを積極活用し、自社のアプリで買い物客をオンラインないしオフラインの店舗に誘導しています。FedexやUPSは荷物の追跡コードに長い文字列を入力する代わりに、スマホアプリで読み取ります。
製品のアクティベートキーの入力や、雑誌や広告の製品番号を検索するなど、モバイルサイトの訪問者に長い文字列を手入力させるなら、QRコードを活用して、入力ミスを確認する手間を減らしましょう。
モバイルサイトでQRコードを読み取る方法
QRコードの読み取りにネイティブアプリは不要です。カメラ付きのスマートフォンとJavaScriptを実行できるブラウザーがあれば、QRコードリーダーは簡単に自作できます。
組み込み型QRボタン付きのテキストフォームのデモです。テキスト入力フォームの横にあるQRボタンをタップすると、スマートフォンのカメラが起動します。
ブラウザー要件:iOS 6以上か、Android 3.0以上。そのほかのデバイスではテストしていません。
対応のデバイスがあれば、サンプルを実行してみてください。デバイスやブラウザーによって異なりますが、カメラが起動するか、カメラとアルバムのどちらを開くか聞かれるはずです。非対応のデバイスではファイルエクスプローラーが開きます。
読み取るQRコードが手元になければ、円周率の頭8ケタを表した以下のQRコードを読んでください。
QRコードリーダーの作成
カメラの起動は、input要素にファイル選択(type=file)を指定することで実現できます。accept属性で画像のみ受け付けて、capture属性で既存の画像ではなく撮影すると指定します。
<input type=file
accept="image/*"
capture=environment>
ファイル選択フォームの外観を整える
ファイル選択の要素はスタイル付けが難しく、場所を取るので、小さなアイコンにします。要素を非表示にして、label要素で包んで実現します。label要素によってユーザーのタップに反応する領域が作られ、QRアイコンを置く領域も確保できます。
tabキーの操作で誤ってアップロードボタンを選択させないためにtabindex=-1とtabindex属性に-1を指定することで、非表示にします。
<label class=qrcode-text-btn>
<input type=file
accept="image/*"
capture=environment
tabindex=-1>
</label>
input要素の外側のlabel要素にクラス名qrcode-text-btnを付けたのは、以下のCSSで両方の要素にスタイルを付けるためです。
.qrcode-text-btn {
display: inline-block;
height: 1em;
width: 1em;
background: url(qr_icon.svg) 50% 50% no-repeat;
cursor: pointer;
}
.qrcode-text-btn > input[type=file] {
position: absolute;
overflow: hidden;
width: 1px;
height: 1px;
opacity: 0;
}
上記のCSSでは、label要素にdisplay:inline-blockを指定することで、要素の高さと幅を調整できます。
backgroundで背景画像を指定し、訪問者から見えるQRアイコンを設置します。
上記CSSの後半では、子セレクタ(>)で要素の型にファイル選択を指定し、いくつかのCSSプロパティで要素を非表示にします。
position:absoluteを指定して、通常のドキュメントフローから切り離し、この要素が存在しないかのように、ほかの要素が上または下に重なって表示されます。
要素が誤ってページのほかの部分を覆い隠さないように、大きさをwidth:1px、height:1pxと指定しました。
透明度をopacity:0で透明にしました。display:noneで非表示にすると、ブラウザーによってはこのファイル選択要素が反応しなくなります。
テキスト入力要素の製作
QRコードリーダーの起動ボタンを置き、テキスト入力要素を作ります。
<input type=text class=qrcode-text
><label class=qrcode-text-btn>
<input type=file
accept="image/*"
capture=environment
tabindex=-1>
</label>
ヒント:
2つのインライン要素の間に空白行やスペースがあると、意図しない位置ずれが発生します。上記では空白を削除するためにHTMLタグの終端括弧(>)を次の行に移して、あとに続く要素の開始括弧(<)と密着しています。
この時点でのQRコードリーダーの外観です。
QRボタンはテキスト入力要素の右にあります。内側に表示するには、CSSで左に移動させテキスト入力欄と重ねます。
.qrcode-text {
padding-right: 1.7em;
margin-right: 0;
vertical-align: middle;
}
.qrcode-text + .qrcode-text-btn {
width: 1.7em;
margin-left: -1.7em;
vertical-align: middle;
}
最初のCSSブロックで、テキスト要素の右側に空白を作りQRボタンの場所を確保します。paddingで、テキスト要素内の文字はQRボタンが重なるのを防ぎます。
テキスト要素の余白が初期値のままではずれるので、margin-right:0を指定します。
2番目のCSSブロックで、隣接要素セレクタ(+)でQRボタンを選択し、左にずらしてテキスト要素と重ねます。
隣接要素セレクタでは、QRボタンがテキスト要素の次にある場合のみCSSが適用されます。QRボタンを別の場所で再利用したとき、位置関係が異なるとQRボタンが意図した位置に表示されません。
テキスト要素とQRボタンの両方にvertical-align:middleを指定し、ボタンをテキストボックスの天地中央にします。初期では上側に位置し、inline-block要素の上下位置は底面が周囲のインライン要素のベースラインに沿います。
QRボタンの幅をwidth of 1.7emと大きくして、タッチ対象領域を確保しています。アイコンは非表示にしたボタンの中央に表示した画像なので、QRアイコンは大きくなりません。以下の画像では、クリックに反応する領域を確認するために、ボタンの背景色を緑色にしています。
JavaScriptライブラリーの読み込み
QRコード読み取りの仕組みは数学的で難しいですが、オープンソースのライブラリーがあります。ZXing制作のJavaベース画像処理ライブラリーのJavaScript版を使用しましょう。JavaScript版はLazar Laszloによって移植されました。
JavaScriptライブラリーには17個のファイルがありますが、1ファイルにまとめ、コードを匿名関数で包むことでグローバル名前空間の汚染(名称重複)を防ぎ、Google Glosureのサイズ削減ツールでファイルを小さくできます。
ライブラリーを改変する
ライブラリーを適用しやすいよう出力関数に小さな変更を加え、successとerrorのレスポンスを区別します。
変更は、qrcode.jsの以下の2行に実装します。
qrcode.result = "error decoding QR Code";
//...
qrcode.callback("Failed to load the image");
Errorオブジェクトに置き換えます。
qrcode.result = Error("error decoding QR Code");
//...
qrcode.callback(Error("Failed to load the image"));
コールバック関数のペイロードがErrorのインスタンスか確認すればエラーの発生が分かります。
変更を加えたコードはこのフォークです。
Scriptタグの追加
QRコードリーダーにライブラリーを使用するには、HTMLでscriptタグを使って以下を読み込みます。
<script src="https://rawgit.com/sitepoint-editors/jsqrcode/master/src/qr_packed.js">
</script>
イベントハンドラに関連付ける
ファイル選択ボタンのonchangeイベントにイベントハンドラを設けます。
<input type=file
accept="image/*"
capture=environment
onchange="openQRCamera(this);"
tabindex=-1>
ファイル選択要素(input要素)で画像を読み込み、QRライブラリーに渡すJavaScriptの関数を書きます。
function openQRCamera(node) {
var reader = new FileReader();
reader.onload = function() {
node.value = "";
qrcode.callback = function(res) {
if(res instanceof Error) {
alert("No QR code found. Please make sure the QR code is within the camera's frame and try again.");
} else {
node.parentNode.previousElementSibling.value = res;
}
};
qrcode.decode(reader.result);
};
reader.readAsDataURL(node.files[0]);
}
ファイルのバイナリデータを読むFileReaderオブジェクトを作りました。
FileReaderがファイルの読み込みに成功したら、コールバック関数でファイルの中身をQRライブラリーのdecode関数に渡します。ライブラリーはコールバック関数を実行し、ErrorオブジェクトもしくはQRコードから得た値を文字列として返します。
成功したら、テキスト要素にQRコードから得た値をセットします。エラーなら警告を表示し、もう一度試すようユーザーに促します。
初心者向けの案内を表示する
QRコードリーダーを初心者でも直感的に使えるようにするため、ファイル選択ボタンのonclickイベントでカメラ起動前に警告を表示して、カメラの使い方を説明します。
<input type=file
accept="image/*"
capture=environment
onclick="return showQRIntro();"
onchange="openQRCamera(this);"
tabindex=-1>
警告の代わりに確認ダイアログを表示し、説明を読んだあとユーザーが望めば中止できるようにします。
function showQRIntro() {
return confirm("Use your camera to take a picture of a QR code.");
}
QRコードリーダーの動作を確認する
QRコードリーダーが完成しました。
非対応のブラウザー対応
現状ではQRボタンは非対応のブラウザーにも表示されるので、非対応の場合には隠す処理を加えます。
capture属性に対応するのはiOSのみなのでJavaScriptで機能のサポートを検出できません。Androidはカメラを含む選択肢が表示される、WebKit派生のモバイルブラウザーの挙動を念頭に置かなければなりません。WindowsタブレットやPCではファイルエクスプローラーが開きますが、カメラを選択できないので対処方法はありません。
iOSでもAndroidでもないスマートフォンのシェアはIDCによればわずか0.7%しかなく(2016年11月時点)、大きさや重さを考えるとタブレットやPCで写真を撮るのは難しいため、「画面のサイズを検出してスマートフォンの場合にだけQRボタンを表示する」のが現実的です。幅750ピクセルを境目として、iPhone 6 Plus(幅736ピクセル)に対応しつつネットブック(縦にしたときの幅800ピクセル)を除外します。
CSSのメディアクエリで実現します。
CSSメディアクエリを使う
メディアクエリを定義し、幅750ピクセル以下のデバイス用にCSSを適用します。
@media only screen and (max-device-width:750px) {
/* previous CSS code goes here */
}
上記の括弧内に、以前書いたCSSコードをすべて含めて、デバイスの幅が750ピクセル以下の場合にのみCSSを適用します。
メディアクエリの外側にも少しCSSコードを追加し、初期状態ではQRボタンが隠します。以下のコードをメディアクエリ部分の直前に配置します。
.qrcode-text-btn {
display: none;
}
@media only screen and (max-device-width:750px) {
/* ... */
デザインの一貫性を保つ
必須ではありませんが、メディアクエリの外側に少しCSSを追加することで対応ブラウザーでも非対応ブラウザーでもテキスト入力欄が一貫した挙動と外観になるようにします。
.qrcode-text {
box-sizing: border-box;
vertical-align: middle;
}
@media only screen and (max-device-width:750px) {
/* ... */
qrcode-text要素にbox-sizing:border-boxを指定して、要素の幅の算出にパディングを含めます。これでテキスト要素にパディングが追加された場合(前回padding-right:1.7emと設定しましたね)、テキスト要素の幅は広がらずに、内部に収まります。
またvertical-align:middleを指定し、垂直方向の位置が対応デバイス・非対応デバイスともに同じにしましす。
最後に
モバイルサイトに自前のQRコードリーダーを設置できました。
QRコードはもう何年も前からあり、ZXingによる画像処理コードも最初にJavaScriptに移植されてから約6年が経ちます。JavaScript版の作者FileReaderはこのアイデアを実証するために、さまざまなHTML技術を駆使した画像キャプチャとQRコード処理をサイトに掲載しています。
紹介した技術は、ユーザーにとって魅力的でフレンドリーになるよう現行技術を応用したものにすぎません。
今回のQRコードリーダーにはまだ改善の余地があります。カメラ画像をキャプチャするのにより万能なgetUserMediaも使えます。ただしこの技術は新しいため古いiPhoneやAndroidに対応していません。
画像処理コードも、さらに高速な認識とより優れた背景ノイズ処理を目指して改良できるかもしれません。将来の記事でまた紹介できたら良いと思います。
ダウンロード可能なリソース
参考リンク
- 画像キャプチャについてのW3仕様
- CanIUse.comによるImage Captureのブラウザー対応一覧表
- Lazar Laszloの実証
- JavaScriptライブラリーのフォーク(Github)
- ZXingの画像処理ライブラリー
本記事はGiulio Mainardiが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。
(原文:How to Create a QR Code Reader for Your Mobile Website)
[翻訳:西尾 健史/編集:Livit]