1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

コピペでOK!はてなブログの画像に「マウスホバーで虫眼鏡ズーム」を実装する方法(JS/CSS)

1
Last updated at Posted at 2026-03-25

スクリーンショット 2026-03-25 110255.png

はじめに

技術ブログやレビュー記事を書いているとき、「画像の一部をもっと細かく見せたい」と思ったことはありませんか?
わざわざ拡大画像をクリックして別タブで開くのは、読者にとってストレスです。

そこで今回は、画像にマウスを合わせるだけで、その場が虫眼鏡のように拡大される機能を実装しました。外部ライブラリは一切不要、CSSとVanilla JS(素のJavaScript)だけで動作します。

実際の動作イメージ

記事内の画像にカーソルを合わせると、円形のレンズが出現。

マウスの動きに合わせて、画像内の対応する箇所が拡大表示される。

画像から外れるとレンズは消える。

実装コード

以下のコードを、はてなブログの「デザイン」→「カスタマイズ」→「記事上下カスタマイズ」の「記事下」などに貼り付けるだけで動作します。

  1. スタイル定義(CSS)
    拡大鏡の外観を整えます。
style.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)

マウス座標と背景画像のポジションを計算するロジックです。

zoom.js
<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>

技術的なポイント

  1. pointer-events: none の活用
    拡大鏡(レンズ)がマウスの真上に表示されるため、これがないとマウスイベントがレンズ自身に奪われ、下の画像座標が取れなくなってしまいます。これを指定することで、レンズ越しに画像を触っている状態を作っています。

  2. background-position による擬似拡大
    実際に画像を拡大しているのではなく、レンズ要素の background-image に同じ画像を指定し、マウスの移動距離に拡大率(ZOOM)を掛け合わせた分だけ背景を逆方向に動かすことで、拡大されているように見せています。

  3. パフォーマンスへの配慮
    全ての画像にイベントリスナーを貼るのではなく、document に対して一つだけ mousemove を設定する「イベントデリゲーション」に近い形を採用しています。これにより、記事内に大量の画像があっても動作が重くなりません。

まとめ

スクリプトを1つ入れるだけで、読者の体験(UX)をグッと向上させることができます。特にはてなブログなどのブログサービスでは、CSSとJSの自由度を活かしたこうしたカスタマイズが効果的です。

ぜひ自分のブログに合わせて、ZOOM 倍率やレンズのサイズを調整して使ってみてください!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?