本文へスキップ
Articles

2026-08-21 公開 / Webツール

「2回目のタップだけ効かない」地図の不具合。原因は2つのアニメーションが打ち消し合っていた

地図と店舗一覧を並べたツールを作りました。一覧の店をタップすると、地図がその店の位置へ移動します。1回目は動くのに、2回目以降はタップしても地図が止まったままでした。「n回目だけおかしい」は原因が見えにくい部類です。切り分けの手順と、結局どこが競合していたのかを書きます。

症状: 1回目は動く。2回目からは反応しない

再現手順はこうでした。

  1. 一覧から店Aをタップする → 地図がAへ移動して、Aの吹き出しが開く(正常)
  2. 続けて店Bをタップする → 吹き出しはBに変わるが、地図が動かない
  3. 以降、何回タップしても地図は動かない

エラーは出ません。タップ自体は効いていて、吹き出しの中身も正しく切り替わります。「動くべきものが黙って動かない」だけなので、コンソールを見ても手がかりがありませんでした。

「1回目だけ動く」が意味すること

ここで先に考えたのは、コードの中身ではなく1回目と2回目で何が違うかでした。同じ関数を呼んでいるのに結果が違うなら、違いは引数か、呼ぶ前の状態のどちらかにあります。

引数は毎回違う店の座標なので、そこは問題なさそうでした。残るのは状態です。並べてみると1つ見つかりました。

つまり1回目だけ「ズームの変化」を伴っていました。同じ「移動」でも、拡大を伴う移動と、同じ拡大率のまま平行移動するのとでは、内部で通る処理が違います。ここが怪しいと当たりがつきました。

「n回目だけ」の不具合は、たいてい状態の差です。1回目と2回目で何が変わっているかを箇条書きにするだけで、候補がかなり絞れます。コードを読み始めるのはそのあとで足ります。

真因: 移動の演出を、吹き出しが打ち消していた

このツールでは、タップされたとき2つのことを続けて実行していました。

  1. 地図をその店の位置へ移動する(滑らかに滑るアニメーション付き)
  2. その店の吹き出しを開く

問題は2番目でした。地図ライブラリの吹き出しには、「開いたとき、吹き出しが画面内に収まるように地図を少しずらす」機能があります。親切な機能ですが、これも地図を動かす操作です。

つまり1のアニメーションが走っている最中に、2が別の移動を始めていました。後から始まった移動が前の移動を打ち消し、結果として地図はほとんど動かないまま止まります。

なぜ1回目だけ無事だったのか。拡大を伴う移動は、同じ拡大率での平行移動とは処理の経路も所要時間も違い、吹き出しの調整が入り込む前に移動が確定していたためです。2回目以降は平行移動だけになり、両者が同じ土俵でぶつかるようになった、という順序でした。

直し方: 演出をやめて、順番を確定させる

直したのは実質2行です。移動をアニメーションなしで即座に確定させてから、吹き出しを開くようにしました。

// 移動を同期的に確定させる。アニメーションのまま吹き出しを開くと、
// 吹き出し側の自動調整に移動が打ち消されて「動かない」ように見える
map.setView([r.lat, r.lng], Math.max(map.getZoom(), 16), { animate: false });

// 確定してから、その店の吹き出しを開く
const mk = markerOf.get(r);
if (mk) { mk.openPopup(); }

滑らかに滑る演出は失われますが、タップした場所へ確実に飛ぶほうが、この道具では正しいと判断しました。使う人は地図の動きを鑑賞したいのではなく、店の場所を知りたいだけです。

もう1つ、Math.max(map.getZoom(), 16) の部分にも判断があります。移動のたびに拡大率を16に固定してしまうと、利用者が自分で拡大して見ていた状態を勝手に引き戻すことになります。そこで「今の拡大率と16の、大きいほう」を使い、寄って見ている人はその寄りを維持するようにしました。

ついでに直した2つの挙動

1. タップのたびにページ最上部へ飛んでいた

元の作りでは、一覧をタップすると地図を見せるためにページの最上部までスクロールしていました。地図が画面外にあるときは親切ですが、すでに地図が見えているときは、余計に画面が飛ぶだけです。一覧を上から順に見ていく人には、毎回スクロール位置がリセットされてしまいます。

「地図が画面外にあるときだけ寄せる」に変えました。自動スクロールは、必要な条件を書けるときだけ入れるものだと思っています。

2. 一覧には出るのに、地図に印が無い店があった

このツールは加盟店を地図に点で表示しますが、件数が多いため表示する点の数に上限(700件)を設けています。上限を超えた店は一覧には載るのに地図上の点が存在せず、タップしても開く吹き出しがありませんでした。

この場合はその場で同じ内容の吹き出しを作って表示するようにしました。上限を上げると、端末によっては地図が重くなります。上限は上限として残したまま、そこからこぼれた店だけを個別に救うほうが、全体としては軽く済みます。

持ち帰れること

ちなみに、この直しのときにコードへ「なぜアニメーションを切ったのか」を英語のコメントで残しました。切った理由が書いていないと、次に見た人が「アニメーションが無いのは手抜きだ」と思って親切に戻してしまいます。そうなると2回目のタップがまた効かなくなります。あえて外したものには、外した理由を添えるのが結論でした。