HTML5 Canvasで画像の一部をルーペ風に拡大表示
ECサイトなどで見かける画像の一部をルーペで見たように拡大する機能ですが、従来はJS+CSSで実装される事が多かったのでないでしょうか。
上記機能をCanvasで実現できると考え、早速作成しました。
デモ
下記画像のAとBのタイルは物理的には同じ色ですが、チェックや円柱からなる影の影響でBが薄く見える有名な錯視画像です。
拡大時はわかりやすいようにAとBをラインでつなげていますので、拡大してご覧ください。
- 対応ブラウザ
-
- IE9+
- Chrome(最新版を推奨)
- Firefox(最新版を推奨)
- Safari(最新版を推奨)
仕組み
マウス移動と連動して表示範囲を算出し、拡大表示領域に表示する。という基本的な仕組みは共通ですが拡大表示部分の実現方法が異なります。
- Canvas版
-
- マウス位置に応じて拡大表示範囲を算出
- canvas要素へdrawImage()で画像を描画
具体的なコードは上記デモのJSタブでご覧ください。
- 従来版(JS+CSS)
-
- 拡大表示領域に背景画像として拡大画像をセット
- マウス移動と連動して背景画像の位置(background-position)を変化させる事で部分的な拡大表示を実現
Canvas版のメリット・デメリット
canvas要素で実現するメリットですが、Canvasの各種APIによりブラウザ上で画像加工(表示を変化)出来る事がメリットでしょうか。
閲覧者のアクションに応じて表示を変化させるなど双方向性のあるコンテンツを作成する状況で重宝しそうです。
(従来版でも画像を複数パターン用意することで可能ですが、画像作成の手間が増えます)
では反対にデメリットですが、やはりCanvas非対応の環境で機能しないことが一番のデメリットです。
また、今回のようなシンプルな機能であれば然程問題になりませんが、モバイルデバイスではパフォーマンスの問題が生じる可能性があります。
ハマったポイント
画像上にルーペレイヤー(canvas要素)を重ねると、canvas要素上にカーソルが乗っている間はmousemoveイベントが発火しなくなります。
つまり、下記のように一定距離でしか移動出来ず、結果的にカクカクとした動きに見えます。
- 画像上でカーソルを動かす
- ルーペがカーソル位置まで移動
- カーソルがルーペ(canvas要素)の上に乗る
- ルーペの円(canvas要素)からカーソルが出るまでイベントが発火(ルーペが移動)しない
- 結果的に、一定距離でしかルーペが動かせない
この問題を解決するため、画像(img要素)<ルーペ(canvas要素)<イベント発火用の空div要素と3重のレイヤーとして、ルーペは空div要素上のカーソル位置を基準に移動させる事にしました。
IE以外は上記で解決しましたが、IEの場合空要素ではイベントが発火しないようです。
空要素でイベントが発火しない問題について、テキストノードを挿入するとその部分だけイベントが発火するようになりました。
この事から空要素に背景色を設定する事で、空要素全体でイベントが発火するようになりましたが、最上位にレイヤーを配置しているため背景色は透明である必要があります。
結論としては下記CSSを空要素に適用することで透明の最上位レイヤーとして、イベントを発火させる事が出来ます。
/* IE9で空div要素のイベントが発火しない対策 */ div.helperDiv { background-color: #fff; opacity: 0; }
今後
今回作成したシンプルな機能(拡大表示)では従来型と表面上の違いがありません。
Canvasが利用出来る状況はまだ限られますが、ノウハウ蓄積を目的としてCanvasならではの表現力を追求したいと考えています。
また汎用的に使えるようオプションを充実させたいところです。