はじめに
技術ブログやレビュー記事を書いているとき、「画像の一部をもっと細かく見せたい」と思ったことはありませんか?
わざわざ拡大画像をクリックして別タブで開くのは、読者にとってストレスです。
そこで今回は、画像にマウスを合わせるだけで、その場が虫眼鏡のように拡大される機能を実装しました。外部ライブラリは一切不要、CSSとVanilla JS(素のJavaScript)だけで動作します。
実際の動作イメージ
記事内の画像にカーソルを合わせると、円形のレンズが出現。
マウスの動きに合わせて、画像内の対応する箇所が拡大表示される。
画像から外れるとレンズは消える。
実装コード
以下のコードを、はてなブログの「デザイン」→「カスタマイズ」→「記事上下カスタマイズ」の「記事下」などに貼り付けるだけで動作します。
- スタイル定義(CSS)
拡大鏡の外観を整えます。
<style>
/* 拡大鏡本体(レンズ)のスタイル */
.gtm-image-magnifier-lens {
position: absolute;
width: 180px;
height: 180px;
border: 3px solid rgba(255,255,255,0.95);
border-radius: 50%;
box-shadow: 0 6px 18px rgba(0,0,0,0.25);
cursor: none;
pointer-events: none; /* マウスイベントを透過させて背後の画像座標を取得 */
z-index: 100000;
display: none;
background-repeat: no-repeat;
background-color: #fff;
box-sizing: border-box;
}
/* 記事内の画像にホバーした時にカーソルを変える */
.entry-content img {
cursor: zoom-in;
}
</style>
ロジック(JavaScript)
マウス座標と背景画像のポジションを計算するロジックです。
<script>
(function() {
'use strict';
// 二重読み込み防止
if (window.__gtmImgMagLoaded) return;
window.__gtmImgMagLoaded = true;
const ZOOM = 2.2; // 拡大率
const LENS_SIZE = 180; // レンズの大きさ
const lens = document.createElement('div');
lens.className = 'gtm-image-magnifier-lens';
document.body.appendChild(lens);
const handleMouseMove = function(e) {
const el = e.target;
// 特定のクラス(.entry-content等)配下の画像のみを対象にする
const isImg = el.tagName === 'IMG' && (
el.closest('.entry-content') ||
el.closest('.article-entry')
);
if (!isImg) {
lens.style.display = 'none';
return;
}
const rect = el.getBoundingClientRect();
const cx = e.clientX;
const cy = e.clientY;
// 画像内での相対座標を計算
const x = cx - rect.left;
const y = cy - rect.bottom; // ※座標計算の基準点
const half = LENS_SIZE / 2;
const px = window.pageXOffset || document.documentElement.scrollLeft;
const py = window.pageYOffset || document.documentElement.scrollTop;
// レンズの位置をマウスに追従させる
lens.style.display = 'block';
lens.style.left = (px + cx - half) + 'px';
lens.style.top = (py + cy - half) + 'px';
// レンズ内の背景画像を拡大・移動させる
// 仕組み:背景画像として同じ画像を指定し、background-positionで表示位置をズラす
lens.style.backgroundImage = 'url(' + (el.currentSrc || el.src) + ')';
lens.style.backgroundSize = (el.width * ZOOM) + 'px ' + (el.height * ZOOM) + 'px';
lens.style.backgroundPosition = (-((x * ZOOM) - half)) + 'px ' + (-((y * ZOOM) - half)) + 'px';
};
const handleScroll = function() {
lens.style.display = 'none';
};
document.addEventListener('mousemove', handleMouseMove, false);
window.addEventListener('scroll', handleScroll, false);
})();
</script>
技術的なポイント
-
pointer-events: none の活用
拡大鏡(レンズ)がマウスの真上に表示されるため、これがないとマウスイベントがレンズ自身に奪われ、下の画像座標が取れなくなってしまいます。これを指定することで、レンズ越しに画像を触っている状態を作っています。 -
background-position による擬似拡大
実際に画像を拡大しているのではなく、レンズ要素の background-image に同じ画像を指定し、マウスの移動距離に拡大率(ZOOM)を掛け合わせた分だけ背景を逆方向に動かすことで、拡大されているように見せています。 -
パフォーマンスへの配慮
全ての画像にイベントリスナーを貼るのではなく、document に対して一つだけ mousemove を設定する「イベントデリゲーション」に近い形を採用しています。これにより、記事内に大量の画像があっても動作が重くなりません。
まとめ
スクリプトを1つ入れるだけで、読者の体験(UX)をグッと向上させることができます。特にはてなブログなどのブログサービスでは、CSSとJSの自由度を活かしたこうしたカスタマイズが効果的です。
ぜひ自分のブログに合わせて、ZOOM 倍率やレンズのサイズを調整して使ってみてください!
