d3. js onclick and touch работает беспорядочно на карте topo json - PullRequest
2 голосов
/ 25 февраля 2020

Я играл с d3. js уже некоторое время, и я пытался создать карту d3, в которой при щелчке по различным районам / округам на карте штата отображаются подробные сведения о районах / округах рядом с картой. .

Первоначально я использовал mouseover и mouseout для отображения того же, но это не было удобно для мобильных устройств. Так что теперь я пытаюсь сделать то же самое с onclick, но он не работает таким же образом.

Район должен менять цвет при клике (он работал с mouseover). Однако он меняет цвет только после нескольких повторных случайных нажатий внутри района.

Это то, что я сделал.

    var width = 345,
    height = 450;
  var projection = d3.geoMercator()
    .center([88.36, 27.58])
    .translate([width / 2, height / 2])
    .scale(6000);
  var path = d3.geoPath()
    .projection(projection);

  var svg = d3.select('#Sk_Map').append('svg')
    .attr('width', width)
    .attr('height', height);

  var g = svg.append('g');

  d3.json('https://raw.githubusercontent.com/shklnrj/IndiaStateTopojsonFiles/master/Sikkim.topojson')
    .then(state => {

      g.append('path')
        .datum(topojson.merge(state, state.objects.Sikkim.geometries))
        .attr('class', 'land')
        .attr('d', path);

      g.append('path')
        .datum(topojson.mesh(state, state.objects.Sikkim, (a, b) => a !== b))
        .attr('class', 'boundary')
        .attr('d', path);

      g.append("g")
        .selectAll("path")
        .data(topojson.feature(state, state.objects.Sikkim).features)
        .enter()
        .append("path")
        .attr("d", path)
        .attr("class","boundary")

        //.on("mouseover", function(d)){

        .on("click", function(d) {
          var prop = d.properties;

          var string = "<p><strong>District Name</strong>: " + prop.Dist_Name;

          d3.select("#Place_Details")
            .html("")
            .append("text")
            .html(string);
       d3.select(this).attr("class","boundary hover");

        })

            //.on("mouseout"), function(d){

        .on("click", function(d) {
        d3.select("h2").text("");
        d3.select(this).attr("class","boundary")
              .attr("fill", "#ff1a75");
        });
    });
.columns {
    float: left;
    width: 50%;
}

/* Clear floats after the columns */
    .mapcontainer:after {
    content: "";
    display: table;
    clear: both;
}

svg {
      background: #ffffff;
    }

    .land {
      fill: #ff1a75;
    }

    .boundary {
      fill: none;
      stroke: #00ffff;
      stroke-linejoin: round;
      stroke-linecap: round;
      stroke-width: 1px;
      vector-effect: non-scaling-stroke;
    }

h2 {
        top: 50px;
        font-size: 1.6em;
    }
    .hover {
        fill: yellow;
        }
 <script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
    <script src="https://unpkg.com/topojson@3" charset="utf-8"></script>
    <div id="Sk_Map" style="width: 300px; float:left; height:450px; margin:5px"></div>
    <div id="Place_Details" style="width: 400px; float:right; height:450px; overflow: auto; margin:5px"></div>
  

Как оптимизировать этот код? Я sh добавлю функцию масштабирования на карту, но сейчас я хочу отобразить название района / округа.

1 Ответ

2 голосов
/ 25 февраля 2020

В вашем коде есть несколько проблем.

В настоящее время вы назначаете двум прослушивателям событий один и тот же выбор:

  g.append("g")
    .selectAll("path")
    .data(topojson.feature(state, state.objects.Sikkim).features)
    .enter()
    .append("path")
    ...
    .on("click", function(d) {
       /* on click code here */
    })
    .on("click", function(d) {
      /* on click code here too */
    });

При назначении прослушивателей событий, подобных этому, второй перезаписывает первый. Таким образом, единственный прослушиватель событий в вашем фрагменте, который используется, является вторым:

    .on("click", function(d) {
       d3.select("h2").text("");
       d3.select(this).attr("class","boundary")
         .attr("fill", "#ff1a75");
    });

Поскольку у вас нет элемента h2 (по крайней мере, во фрагменте), ничего не происходит.

Если мы отбросим второго прослушивателя событий и используем только первое, мы все равно не получим большую часть события при нажатии. Ниже я удаляю другие функции (те, у которых нет событий щелчка, , а также удаляю несвязанные css, изменяю размер фрагмента и изменяю цвет обводки элемента ). Должно быть понятно, почему событие щелчка работает не очень хорошо, функции не заполнены. Только щелчок запускает событие на границе:

var width = 345,
    height = 300;
var projection = d3.geoMercator()
  .center([88.36, 27.58])
  .translate([width / 2, height / 2])
  .scale(7000);
var path = d3.geoPath()
  .projection(projection);

var svg = d3.select('#Sk_Map').append('svg')
  .attr('width', width)
  .attr('height', height);

var g = svg.append('g');

d3.json('https://raw.githubusercontent.com/shklnrj/IndiaStateTopojsonFiles/master/Sikkim.topojson')
    .then(state => {

      g.append("g")
        .selectAll("path")
        .data(topojson.feature(state, state.objects.Sikkim).features)
        .enter()
        .append("path")
        .attr("d", path)
        .attr("class","boundary")
        .on("click", function(d) {
          alert("click!");
        })
});
    .boundary {
      fill: none;
      stroke: black;
      stroke-linejoin: round;
      stroke-linecap: round;
      stroke-width: 1px;
      vector-effect: non-scaling-stroke;
    }
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
    <script src="https://unpkg.com/topojson@3" charset="utf-8"></script>
    <div id="Place_Details"></div>
    <div id="Sk_Map" style="width: 300px; float:left; height:200px; margin:5px"></div>

Решение состоит в том, чтобы наполнить функции. Это приводит нас к оптимизации: нам не нужно рисовать первую функцию:

  g.append('path')
    .datum(topojson.merge(state, state.objects.Sikkim.geometries))
    .attr('class', 'land')
    .attr('d', path);

Поскольку она будет полностью покрыта интерактивными функциями.

Кроме того, если мы хотим, чтобы чтобы границы не были кликабельны, мы должны нарисовать эту функцию:

  g.append('path')
    .datum(topojson.mesh(state, state.objects.Sikkim, (a, b) => a !== b))
    .attr('class', 'boundary')
    .attr('d', path);

После того, как мы нарисуем кликабельные объекты, чтобы они нарисовались сверху. Если нам не важно, можно ли щелкнуть по границам, мы можем пропустить рисование этой функции, поскольку мы можем просто применить обводку к интерактивным объектам. Хотя внутренние границы могут быть несколько толще / темнее.

Вот вышеупомянутые модификации:

var width = 345,
    height = 300;
var projection = d3.geoMercator()
  .center([88.36, 27.58])
  .translate([width / 2, height / 2])
  .scale(7000);
var path = d3.geoPath()
  .projection(projection);

var svg = d3.select('#Sk_Map').append('svg')
  .attr('width', width)
  .attr('height', height);

var g = svg.append('g');

d3.json('https://raw.githubusercontent.com/shklnrj/IndiaStateTopojsonFiles/master/Sikkim.topojson')
    .then(state => {

      g.append("g")
        .selectAll("path")
        .data(topojson.feature(state, state.objects.Sikkim).features)
        .enter()
        .append("path")
        .attr("d", path)
        .attr("class","feature")
        .on("click", function(d) {
             var prop = d.properties;
             var string = "<p><strong>District Name</strong>: " + prop.Dist_Name;
             d3.select("#Place_Details")
               .html(string)
        })
        
      g.append('path')
        .datum(topojson.mesh(state, state.objects.Sikkim, (a, b) => a !== b))
        .attr('class', 'boundary')
        .attr('d', path);        
        
});
.boundary {
   fill: none;
   stroke: #00ffff;
   stroke-linejoin: round;
   stroke-linecap: round;
   stroke-width: 1px;
   vector-effect: non-scaling-stroke;
}

.feature {
   fill: steelblue;
}


.hover {
   fill: yellow;
}
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
    <script src="https://unpkg.com/topojson@3" charset="utf-8"></script>
    <div id="Place_Details"></div>
    <div id="Sk_Map" style="width: 300px; float:left; height:200px; margin:5px"></div>

Если у вас есть вопрос о том, как управлять двумя событиями щелчка или чередующимися событиями щелчка по функции, это должен быть новый вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...