LoginSignup
3
6

More than 5 years have passed since last update.

d3.jsで地図をぬるっとアニメーションさせる。

Posted at

前回、d3で気象庁降灰予報を描画しましたが、
https://qiita.com/miyawa-tarou/items/f101f0248cdc5744302d
地図の中心緯度経度やズームなどは一度描画した後でも変えることができます。
また単に変えるのではなく、ぬるっとスムーズにアニメーションしながら動かすことができます。

完成版はこちら:https://miyawa-tarou.github.io/d3_nuru_map/

基本は前回のコードをもとに説明します。今回の説明に邪魔なので桜島に置いた丸は消しています。
https://github.com/miyawa-tarou/d3_nuru_map/tree/01.setting

1.クリックしたらズームするようにする

やることは、初めに設定した

        // 中心座標とスケール
        var projection = d3.geoMercator()
            .center([137, 38])
            .scale(1600)
            .translate([width / 2, height / 2]);

この部分を
pathのtransform属性で再定義するような形になります。

今回鹿児島をクリックしたらズームするという形にします。

            svg.selectAll("path")  // おまじない。というか理解できてない
                .data(topojson.feature(data, data.objects.pref).features)
                .enter() // dataで入れた配列をそれぞれ以下のpathにしていく
                .append("path")
                .attr("d", path) // <path d="">に path()の返り値を入れていく
                .attr("fill", function(d,i){ return d.properties.code === 46 ? "darkgreen" : "green";}) // 鹿児島だけダークグリーン
                .on("click", function(d,i){ // 各pathをクリックすると実行されることを書く
                    if (d.properties.code === 46) { // 鹿児島をクリックしたときに
                        var zoomCenter = projection([130.6, 31.6]); // 中心座標
                        var zoom = 20000/1600; // ズーム
                        svg.selectAll("path").attr("transform", "translate(" + width/2 + "," + height/2 + ")scale(" + zoom + ")translate(" + - zoomCenter[0] + "," + - zoomCenter[1] + ")");
                    }
                });

すべてのpathに対して移動・ズームするため、
svg.selectAll("path")
に対して行っています。classやidなどで指定を限定すれば一部だけ動かすみたいなことも可能です。

この時ズームに20000/1600を入れていますが、ここは「はじめの設定からのズーム比」になります。
なので前回桜島にズームしたときscale(20000)にしたため、初期のズーム1600との比の20000/1600にしています。
もし2度以上移動する場合も、scale()ははじめの1600基準の数値になります(多分)

そしてtransform属性に
translate():おまじないのwidth/2, height/2 を入れる
scale():上で説明した通り
translate():移動先の座標
を入れます(参考:https://developer.mozilla.org/ja/docs/Web/SVG/Attribute/transform

scale()を指定しなければ移動するだけです。1以下にすればズームアウトするはずです。

ashfall1.png

2.降灰予報もズームする

1では地図はズームしましたが、肝心の降灰予報がごみのようにズームされずにそのまま残っています。
これは前回書いたのがpolygonタグだったためで、pathで書けば同様に処理されます。

初めにMをつけて区切り文字をLにしZで終了すると同じものが描画できます。
https://qiita.com/a-ide/items/107c9044d0f4e0354112#%E3%83%91%E3%82%B9

            svg.append('g')
                .attr('class', 'ashFall')
                .append("path")
                .attr("d", 'M' + svgCoordinate.join('L') + 'Z') // "Mx,yLx,yLx,yZ"

ashfall2.png

3.ぬるっと動かす

d3ではズームする際に簡単にアニメーションにすることができます

svg.selectAll("path").transition().duration(2000).attr("transform", ...);

.transition().duration(2000)
を追加しただけです。
duration()の引数が移動の時間でミリ秒になります。

ここが一番楽しいけどQiitaには貼れないのが残念
ちなみに回転とかもできますが、今回はそれはなしで。

また動き方も直線的なのから加速していくのなども指定できます(ease()を挟む)
詳しくは

4.火山灰の枠線をどうにかする

ズームすると線がズームの分太くなります。
なんかきもいのでこれも変更します。
地図の方は影響を避けるために別にアニメーションを指定します。

            svg.select(".ashFall").select('path').transition().duration(2000).attr("stroke-width", 1600/20000).attr("transform", "translate(" + width/2 + "," + height/2 + ")scale(" + zoom + ")translate(" + - zoomCenter[0] + "," + - zoomCenter[1] + ")");

.attr("stroke-width", 1600/20000)を追加しています。
ズーム前はstroke-widthが1だったので、ズームの分細くしている感じです。
二つのアニメーションが同時に動かせなくて、上で設定したtransformよりこちらの設定が勝ってしまうので、transformをこちらにも記載します。
なんかきれいに細くなっていってない気がするが。。。

ashfall3.png

ex.クリッカブルに見せるためにカーソルにする

鹿児島をクリックできる感を持たせるためにカーソルにします。
せっかくなので、都道府県ごとにclassを指定し、鹿児島のclassに対してカーソルがつくスタイルシートを当てます

        svg.selectAll("path")  // おまじない。というか理解できてない
            .data(topojson.feature(data, data.objects.pref).features)
            .enter() // dataで入れた配列をそれぞれ以下のpathにしていく
            .append("path")
            .attr("d", path) // <path d="">に path()の返り値を入れていく
            .attr("class", function(d,i){ return "Prefs Pref" + d.properties.code;}) // すべての都道府県にPrefsクラスを、各都道府県にPref{Code}クラスを指定

<style>
    .Prefs {
        fill: green; /* 緑で塗る */
    }
    .Pref46 { /* 鹿児島限定 */
        fill: darkgreen;
        cursor: pointer;
    }
</style>

3
6
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
3
6