生成AI+スマホで作成「WordPress(ブログ)で使えるJavaScript」 ブログ画面のルーペ

ブログ画面の文字を拡大表示するルーペです。
基本的にはブログ全体ではなくピンポイントでの使用に適しています。
拡大したい文章は下記のブロック「class="zoom-section"」の中に書きます
ブロックは複数個作れます。

ここがルーペで拡大する範囲です。このブロックごとにルーペが独立して動きます。5行を超える長文はルーペの表示がずれていくので1ブロックの文章は短めが無難です。

​画面の左右の端かブロックの間の余白を触れば、通常通りスクロールが可能です。ブロック同士が近すぎると指がルーペに捕まりやすいので、間隔を空けると操作ミスが減ります。

ここもルーペで拡大できます。
ルーペの枠の太さと色はCSSの border: 3px solid #0073aa; の部分で変更できます。

コードエディター 本文

<div class="zoom-section" style="position: relative; touch-action: pan-y; margin-bottom: 30px;">
  <div class="zoom-text" style="line-height: 2.0; font-size: 16px; color: #333;">
    <p>ここがルーペで拡大する範囲です。このブロックごとにルーペが独立して動きます。5行を超える長文はルーペの表示がずれていくので1ブロックの文章は短めが無難です。</p>
  </div>

カスタムJavaScript

(function() {
  // ページ内のすべてのルーペセクションを対象にする
  const sections = document.querySelectorAll('.zoom-section');

  sections.forEach(section => {
    const text = section.querySelector('.zoom-text');
    const lens = section.querySelector('.zoom-lens');
    const copy = section.querySelector('.zoom-copy');
    
    const zoom = 2;
    const offsetY = 120; // 指からの距離

    // 中身の同期
    const sync = () => {
      copy.innerHTML = text.innerHTML;
      copy.style.width = text.offsetWidth + "px";
    };

    const move = (e) => {
      const rect = text.getBoundingClientRect();
      const touch = e.touches ? e.touches[0] : e;

      const x = touch.clientX - rect.left;
      const y = touch.clientY - rect.top;

      if (x < 0 || y < 0 || x > rect.width || y > rect.height) {
        lens.style.display = 'none';
        return;
      }

      lens.style.display = 'block';
      lens.style.left = (x - 80) + 'px';
      lens.style.top = (y - 80 - offsetY) + 'px';

      copy.style.left = (80 - x * zoom) + 'px';
      copy.style.top = (80 - y * zoom) + 'px';

      if (e.cancelable) e.preventDefault();
    };

    // イベント登録
    section.addEventListener('touchstart', (e) => { sync(); move(e); }, {passive: false});
    section.addEventListener('touchmove', move, {passive: false});
    section.addEventListener('touchend', () => lens.style.display = 'none');
    // PC対応
    section.addEventListener('mousemove', move);
    section.addEventListener('mouseleave', () => lens.style.display = 'none');
    
    window.addEventListener('resize', sync);
    sync();
  });
})();

カスタムCSS

/* 共通スタイル(一度書けばOKですが、各ブロックに含めても問題ありません) */
.zoom-section { user-select: none; -webkit-user-select: none; }
.zoom-lens {
  position: absolute;
  width: 160px;
  height: 160px;
  border: 3px solid #0073aa;
  border-radius: 50%;
  display: none;
  pointer-events: none;
  background: #fff;
  z-index: 1000;
  box-shadow: 0 8px 25px rgba(0,0,0,0.3);
  overflow: hidden;
}
.zoom-copy {
  position: absolute;
  transform-origin: 0 0;
  transform: scale(2); /* 2倍拡大 */
}