【问题标题】:d3.js onclick and touch works erratically on topojson mapd3.js onclick 和 touch 在 topojson 地图上工作不稳定
【发布时间】:2020-06-09 09:58:57
【问题描述】:

我一直在玩 d3.js,我一直在尝试创建一个 d3 地图,其中单击州地图上的不同区/县会显示地图旁边的区/县的详细信息。

我最初使用mouseovermouseout 来显示相同​​的内容,但它不适合移动设备。所以现在我正在尝试对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>
  

如何优化此代码?我希望为地图添加缩放功能,但现在我想显示区/县的名称。

【问题讨论】:

    标签: javascript html css json d3.js


    【解决方案1】:

    您的代码中存在一些问题。

    目前您将两个事件侦听器分配给同一个选择:

      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 */
        });
    

    当像这样分配事件监听器时,第二个会覆盖第一个。所以你的 sn-p 中唯一使用的事件监听器是第二个:

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

    由于您没有 h2 元素(至少在 sn-p 中),因此什么也没有发生。

    如果我们删除第二个事件侦听器并只使用第一个,我们仍然不会得到太多的点击事件。在下面,我删除了其他功能(那些没有点击事件的功能,我也删除了不相关的 css,为 sn-p 调整大小,并更改功能描边颜色)。应该清楚为什么单击事件不能很好地工作,功能没有填充。点击只触发边界上的事件:

    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>

    如果您对如何管理两个点击事件或一个功能上的交替点击事件有疑问,那应该是一个新问题。

    【讨论】:

    • 非常感谢。这样可行。我使用两次单击的原因是因为我想在单击时将区域着色为黄色,并在单击其他区域时将其重新绘制为旧颜色,同时将新单击的区域绘制为黄色。
    • 在点击函数中添加了这个,点击事件现在改变颜色。 d3.selectAll('path').style('fill',null); d3.select(this).style("fill","yellow");
    猜你喜欢
    • 2017-05-05
    • 1970-01-01
    • 2014-02-19
    • 2016-08-04
    • 2018-03-01
    • 2015-02-21
    • 2015-08-06
    • 2014-01-24
    • 2016-11-11
    相关资源
    最近更新 更多